如何获取Python模块中所有非导入名称的列表?

时间:2015-04-29 13:16:56

标签: python python-import

给定一个包含以下内容的模块:

import stuff
from foo import Foo
from bar import *

CST = True

def func(): pass

如何定义函数get_defined_objects以便我可以:

print(get_defined_objects('path.to.module'))
{'CST': True, 'func', <function path.to.module.func>}

现在,我能想象的唯一解决方案是读取原始模块文件,使用re.search(r'^(?:def|class )?(\w+)(?:\s*=)?'提取已定义的名称,然后导入模块,并找到与__dict__的交集。

有什么更清洁的东西吗?

3 个答案:

答案 0 :(得分:3)

以下是您使用ast开始的内容。请注意,此代码并未涵盖所有可能的情况,尽管它应该处理例如多次分配正确。例如,如果您想访问已编译的代码,请考虑更密切地研究ast的数据结构和API。

import ast

with open('module.py') as f:
    data = f.read()
    tree = ast.parse(data)
    elements = [el for el in tree.body if type(el) in (ast.Assign, ast.FunctionDef, ast.ClassDef)]

result = {}

for el in elements:
    if type(el) == ast.Assign:
        for t in el.targets:
            if type(el.value) == ast.Call:
                result[t.id] = el.value.func.id + '()'
            else:
                for attr in ['id', 'i', 's']:
                    try:
                        result[t.id] = getattr(el.value, attr)
                        break
                    except Exception as e:
                        pass
    elif type(el) == ast.FunctionDef:
        result[el.name] = '<function %s>' % el.name
    else:
        result[el.name] = '<class %s>' % el.name

print result
#

答案 1 :(得分:3)

mod = "foo"
import ast, inspect
import importlib

mod = importlib.import_module(mod)
p = ast.parse(inspect.getsource(mod))

from collections import defaultdict

data = defaultdict(defaultdict)

for node in p.body:
    if isinstance(node, (ast.ImportFrom, ast.Import)):
        continue
    if isinstance(node, (ast.ClassDef, ast.FunctionDef)):
        data["classes"][node.name] =  mod.__dict__[node.name]
    elif isinstance(node, ast.Assign):
        for trg in node.targets:
            if isinstance(node.value, ast.Num):
                data["assignments"][trg.id] = node.value.n
            elif isinstance(node.value, ast.Str):
                data["assignments"][trg.id] = node.value.s
            else:
                data["assignments"][trg.id] = mod.__dict__[trg.id]

输出:

有一个很好的解释here列出了不同类型的内容及其基于的属性:

class Nodes(ast.NodeVisitor):
    def __init__(self):
        self.data = defaultdict()
        super(Nodes, self).__init__()

    def visit_FunctionDef(self, node):
        self.data[node.name] = mod.__dict__[node.name]
        print("In FunctionDef  with funcion {}".format(node.name))

    def visit_ClassDef(self, node):
        self.data[node.name] = mod.__dict__[node.name]

    def visit_Assign(self, node):
        for trg in node.targets:
            if isinstance(node.value, (ast.Str, ast.Num, ast.Dict, ast.List, ast.ListComp, ast.NameConstant)):
                self.data[trg.id] = mod.__dict__[trg.id]
        self.generic_visit(node)

    def visit_Name(self, node):
        """
        class Name(idctx)
        A variable name. id holds the name as a string
        and ctx is either class Load class Store class Del.
        """
        print("In Name with {}\n".format(node.id))
    #
    def visit_Dict(self, node):
        """
        class Dict(keys, values)
        A dictionary. keys and values
        hold lists of nodes with matching order
        """
        print("In Dict  keys = {}, values = {}\n".format(node.keys,node.values))


    def visit_Set(self,node):
        """
        class Set(elts)
        A set. elts holds a list of
        nodes representing the elements.
        """
        print("In Set  elts = {}\n".format(node.elts))

    def visit_List(self, node):
        """
        class List(eltsctx)
        lts holds a list of nodes representing the elements.
        ctx is Store if the container
        is an assignment target
        (i.e. (x,y)=pt), and Load otherwise.
        """
        print("In List  elts = {}\nctx = {}\n".format(node.elts,node.ctx))

    def visit_Tuple(self, node):
        """
        class Tuple(eltsctx)
        lts holds a list of nodes representing the elements.
        ctx is Store if the container
        is an assignment target
        (i.e. (x,y)=pt), and Load otherwise.
        """
        print("In Tuple  elts = {}\nctx = {}\n".format(node.elts,node.ctx))

    def visit_NameConstant(self, node):
        """
        class NameConstant(value)
        True, False or None. "value" holds one of those constants.
        """
        print("In NameConstant getting value {}\n".format(node.value))


    def visit_Load(self, node):
        print("In Load with node {}\n".format(node.func))


    def visit_Call(self, node):
        """
        class Call(func, args, keywords, starargs, kwargs)
        A function call. func is the function,
        which will often be a Name or Attribute object. Of the arguments:
        args holds a list of the arguments passed by position.
        keywords holds a list of keyword objects representing arguments
        passed by keyword.starargs and kwargs each hold a single node,
        for arguments passed as *args and **kwargs.
        """
        print("In Call with node {}\n".format(node.func))


    def visit_Num(self, node):
        print("In Num getting value {}\n".format(node.n))

    def visit_Str(self, node):
        print("In Str getting value {}\n".format(node.s))
f = Nodes()
f.visit(p)
print(f.data)

答案 2 :(得分:1)

虽然我接受了答案,但发布我最终使用的解决方案并不会有什么坏处。这是其他提案之间的混合:

import ast
import inspect
import importlib

from types import ModuleType

def extract_definitions(module):
    """ Returns the name and value of objects defined at the top level of the given module.

        :param module: A module object or the name of the module to import.
        :return: A dict {'classes': {}, 'functions': {}, 'assignments': {}} containing defined objects in the module.
    """

    if not isinstance(module, ModuleType):
        module = importlib.import_module(module)

    tree = ast.parse(inspect.getsource(module))

    definitions = {'classes': {}, 'functions': {}, 'assignments': {}}

    for node in tree.body:

        if isinstance(node, ast.ClassDef):
            definitions["classes"][node.name] = getattr(module, node.name)
        elif isinstance(node, ast.FunctionDef):
            definitions["functions"][node.name] = getattr(module, node.name)
        elif isinstance(node, ast.Assign):
            # for unpacking, you need to loop on all names
            for target in node.targets:
                definitions["assignments"][target.id] = getattr(module, target.id)

    return definitions

我添加了从字符串或模块对象导入的功能,然后删除了值的解析,并将其替换为原始模块中的简单getattr。