【问题标题】:How to get a list of classes and functions from a python file without importing it如何从 python 文件中获取类和函数的列表而不导入它
【发布时间】:2017-11-25 15:53:12
【问题描述】:

我有一个 python 文件,其中定义了一些类和函数:

class A(object):
    def __init__(self, an_arg, a_default_arg=None):
        pass

def doStuff(an_other_arg, an_other_default_arg=None):
    pass

我想得到这个文件中所有类和函数的列表。 (他们的名字和参数定义就够了)

现在,我知道您可以使用 __import__(module_descriptor)inspect 执行此操作,但这不是一个选项,因为我正在扫描的文件来自不受信任的来源。

我的第一反应是尝试创建一个安全的环境来导入它们,但根据其他 stackoverflow 问题,这似乎是不可能的。

【问题讨论】:

  • 1.您的意思是“函数”,而不是“可执行文件” 2. 为此使用正则表达式似乎很容易。只需像往常一样打开文件并解析它。
  • 不,我特别指的是可执行文件作为由包装器包装的函数通常不再是函数。但为了清楚起见,我会更改它。正则表达式是否不适合解析编程语言,但这种解析可能受到限制?
  • 看看这个正则表达式,例如:regex101.com/r/YPbNcz/1 它很可能可以简化,但它似乎是一个很好的起点。确保使用单行模式,以便. 将匹配换行符。
  • 我发现使用此类正则表达式存在一些问题:1) 它们通常还会返回在多行 cmets 中定义的函数和类(单行 cmets 很容易修复),2) 他们无法检测到某些函数是在同一个文件中用另一种变量类型重新定义。

标签: python class python-import


【解决方案1】:

您可以使用ast 模块来解析源文件,而无需实际执行任何代码。然后可以遍历节点树获取函数和类名/参数。

import ast

def show_info(functionNode):
    print("Function name:", functionNode.name)
    print("Args:")
    for arg in functionNode.args.args:
        #import pdb; pdb.set_trace()
        print("\tParameter name:", arg.arg)


filename = "untrusted.py"
with open(filename) as file:
    node = ast.parse(file.read())

functions = [n for n in node.body if isinstance(n, ast.FunctionDef)]
classes = [n for n in node.body if isinstance(n, ast.ClassDef)]

for function in functions:
    show_info(function)

for class_ in classes:
    print("Class name:", class_.name)
    methods = [n for n in class_.body if isinstance(n, ast.FunctionDef)]
    for method in methods:
        show_info(method)

结果:

Function name: doStuff
Args:
        Parameter name: an_other_arg
        Parameter name: an_other_default_arg
Class name: A
Function name: __init__
Args:
        Parameter name: self
        Parameter name: an_arg
        Parameter name: a_default_arg

【讨论】:

【解决方案2】:

除了实际执行文件之外,没有什么可以给您这个问题的 100% 准确答案。 Python 中动态影响命名空间的方法太多:从其他地方导入名称、有条件地执行定义、通过修改其__dict__ 直接操作命名空间等。

如果您只能使用静态定义,Python 的内置 ast(抽象语法树)模块可能是最简单的解决方案。您可以安全地将文件编译为 AST,然后在其顶层查找 defclass 语句。 (在类的情况下,您将在类主体中寻找def __init__。不要忘记一个类没有自己的__init__,而只是从超类继承一个!)

【讨论】:

    【解决方案3】:

    接受的解决方案不完整。考虑以下文件:

    def regular_function():
        def nested_function():
            pass
    
    async def async_function():
        pass
    

    接受的解决方案只会打印:

    Function name: regular_function
    Args:
    

    要获取所有功能,我们需要进行两处更改:

    1. 遍历整个 AST,而不仅仅是顶级节点
    2. 处理 async 函数以及常规函数

    这里是更正后的代码,用于查找函数:

    import ast
    
    from pathlib import Path
    
    parsed_ast = ast.parse(Path(__file__).read_text())
    
    functions = [
        node
        for node in ast.walk(parsed_ast)
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
    ]
    
    for function in functions:
        print(f"Function name: {function.name}")
        print(f"Args: {', '.join([arg.arg for arg in function.args.args])}")
    

    请注意,这违反了应该使用 AST walk 的范围。对于更复杂的事情,请考虑使用NodeVisitor

    【讨论】:

      猜你喜欢
      • 2018-03-03
      • 2017-08-22
      • 2020-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-28
      • 2011-11-22
      • 1970-01-01
      相关资源
      最近更新 更多