Как получить все переменные, определенные в модуле Python, без импорта?

Например, если у меня следующая структура:

base.py

foo = 1
bar = 2

extended.py

from base import *

baz = 3
qux = 4

Я хочу получить только переменные, определенные в extended.py.


Я пробовал использовать dir и "проверить",

import inspect
import extended
vars = dir(extended)
members = inspect.getmembers(extended)

но это дает vars = ['bar', 'baz', 'foo', 'qux', ...] и members=[('bar', 2), ('baz', 3), ('foo', 1), ('qux', 4), ...]

Есть ли способ сделать это в python, учитывая эту структуру того, как определяется extended.py?

Если бы вы чувствовали себя действительно смелыми, вы могли бы использовать пакет AST для анализа интересующего вас модуля, а затем вытащить из него назначения переменных ...

Tom Dalton 02.05.2018 17:17
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
1
407
3

Ответы 3

Я решил дать этому взору из личного интереса, он, вероятно, сработает для того, что вы хотите, я не смотрел, как работают более сложные задания, например. назначения срезов и т. д.:

import ast

with open("extended.py") as f:
    code = f.read()

tree = ast.parse(code)

assignments = [
    node
    for node in ast.walk(tree)
    if isinstance(node, ast.Assign)
]

target_variables = set()
for a in assignments:
    print("{}: {}".format(a, ast.dump(a)))
    for target in a.targets:
        target_variables.add(target.id)

print("Found {} assignments to these variable names:".format(len(assignments)))
print(target_variables)

Это также содержит назначения в функции, что, вероятно, не то, что он хочет.

MegaIng 02.05.2018 17:44

В приведенном конкретном примере упоминаются только модули, выполняющие from x import *, которые (в отличие от других ответов здесь) этого не отображаются. В вопросе ничего не говорится об определениях функций, поэтому я не делаю никаких предположений о желаемом для них поведении.

Tom Dalton 02.05.2018 17:47

Я нашел несколько простой способ сделать это, возможно, не идеально, но он будет работать:

def find_names_of_module(module):
    code = compile(open(module.__file__).read(), module.__name__, "exec")
    return code.co_names

К сожалению, это также возвращает модули, импортированные с помощью from module import * или import module, а также функции. Вы можете отфильтровать его, чтобы в нем не было модулей и функций, если они вам не нужны.

Если вам нужны только имена переменных верхнего уровня, вот настройка ответа Тома Далтона, которая включает только переменные верхнего уровня:

def find_names_of_module(module):

    with open(module.__file__) as f:
        code = f.read()

    tree = ast.parse(code)

    assignments = (node for node in tree.body if isinstance(node, ast.Assign))
    return {target.id for a in assignments for target in a.targets}

Стоит отметить, что последняя часть не найдет переменные верхнего уровня, которые определены в условном выражении или цикле верхнего уровня, даже if True: foo = 'bar'.

Tom Dalton 02.05.2018 17:49

@TomDalton Так вы говорите, что более вероятно, что у него есть определения переменных внутри операторов if, чем у него есть объявления переменных внутри функций?

MegaIng 02.05.2018 17:51

Я не говорю этого и не утверждаю, что знаю, какая из этих возможностей (если какая-либо из них) верна.

Tom Dalton 02.05.2018 17:52

Если вы можете изменить структуру, я бы пошел с этим решением:

base.py

foo = 1
bar = 2

extension.py

baz = 3
qux = 4

extended.py

from base import *
from extension import *

В противном случае я не уверен, действительно ли этот код работает и должен быть лучший способ, но вы можете использовать разницу между членами модулей:

import inspect
import base
import extended
base_members = inspect.getmembers(base)
ext_members = inspect.getmembers(extended)
ext_only_members = [member for member in ext_members if member not in base_members]

Поскольку содержимое списка не хешируется, вы не можете использовать набор для получения diff.

Другие вопросы по теме