简体   繁体   中英

How to get all of the variables defined in a python module without imports?

For example, if I have the following structure:

base.py

foo = 1
bar = 2

extended.py

from base import *

baz = 3
qux = 4

I'm looking to get only the variables defined in extended.py.


I've tried using dir and 'inspect',

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

but this gives vars = ['bar', 'baz', 'foo', 'qux', ...] and members=[('bar', 2), ('baz', 3), ('foo', 1), ('qux', 4), ...]

Is there anyway to actually do this in python, given this structure of how extended.py is defined?

I decided to give this a bash for personal interest, it will probably work for what you want, I havent' looked at how more complex assignments work eg slice assignments etc:

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)

I found a somewhat simple way of doing it, that is possible not perfect, but it will work:

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

Sadly this does also return modules imported with from module import * or import module and also functions. You could filter it to not have modules and functions if you don't want them.

If you need only the top-level variable names, here is a tweak of Tom Dalton answer that only includes top level variables:

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 you can change the structure I would go with this solution:

base.py

foo = 1
bar = 2

extension.py

baz = 3
qux = 4

extended.py

from base import *
from extension import *

Otherwise, I'm not sure if this code actually works and there must be a better way but you can use the difference between modules members:

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]

Since the list content is not hashable you can't use a set to get the diff.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM