从.py文件中提取Python函数名、函数体和文档类型

2024-03-28 11:40:50 发布

您现在位置:Python中文网/ 问答频道 /正文

我已经用Python编程好几年了,但是,我刚刚了解到您可以使用以下命令:

<function>.__doc__

返回Python函数的docstring!然而,这仍然不足以完成我期待的任务。我需要能够提取特定.py文件中定义的每个函数的名称,并提取函数名docstring和body。例如,假设我有以下.pY文件:

import numpy as np

def get_palindrome(string):
  """Returns the palindrome of the string argument"""
  return string[::-1]

def break_my_computer():
  """Destroys your RAM"""
  a = []
  while True:
    a.append(1)

这应该能够返回以下信息:

info = {1: {
            'name': 'get_palindrome',
            'docstring': 'Returns the palindrome of the string argument',
            'body': 'return string[::-1]'
            },
        2: {
            'name': 'break_my_computer',
            'docstring': 'Destroys your RAM',
            'body': 'a = []\nwhile True:\n  a.append(1)'
        }   }

在Python中获取此信息的最简单方法是什么(我最好不想使用正则表达式库或进行任何文本解析和匹配)

注意:当遇到多行docstring或函数体时,\n(换行)命令应出现在相应的输出中;选项卡应该由命令或空格表示


Tags: 文件ofthe函数命令getstringreturn
1条回答
网友
1楼 · 发布于 2024-03-28 11:40:50

正确的答案是——不要这样做

不管怎样我都要做

你问题的前两部分相当简单,因此我将对第一部分进行反复说明:

import inspect

import module_name as module
# This bit is for if you want to load the module from a file by path, mutually exclusive with previous line
# import importlib
#
# spec = importlib.util.spec_from_file_location("module_name", "/path/to/module_name.py")
# module = importlib.util.module_from_spec(spec)
# spec.loader.exec_module(module)

funcs = []
for name, value in vars(module).items():
    if name.startswith("_") or not callable(value):
        continue
    doc = inspect.getdoc(value)
    code = marshal.dumps(value.__code__)
    funcs.append({"name": name, "docstring": doc, "body": code})

现在我们进入了最难的部分body。直接读取源代码是不可能的,因为Python根本不存储源代码。当然,您可以使用getsource读取文件,但这对于在磁盘上修改的模块不起作用,因为python加载了这些模块。如果要采用此方法,请遵循以下代码:

import inspect

import module_name as module
# This bit is for if you want to load the module from a file by path, mutually exclusive with previous line
# import importlib
#
# spec = importlib.util.spec_from_file_location("module_name", "/path/to/module_name.py")
# module = importlib.util.module_from_spec(spec)
# spec.loader.exec_module(module)

funcs = []
for name, value in vars(module).items():
    if name.startswith("_") or not callable(value):
        continue
    doc = inspect.getdoc(value)
    code = inspect.getsource(value).split(":", maxsplit=1)[1]
    funcs.append({"name": name, "docstring": doc, "body": code})
print(funcs)

这个问题的最佳解决方案是根本不存储源代码。您应该改为使用marshal模块来序列化代码。这个在python的主要版本之间中断,这是一种丑陋的攻击。如果有任何方法可以避免存储函数的代码,请这样做,因为这会带来安全风险

完整代码,包括编组:

import inspect
import marshal
import types

import module_name as module
# This bit is for if you want to load the module from a file by path, mutually exclusive with previous line
# import importlib
#
# spec = importlib.util.spec_from_file_location("module_name", "/path/to/module_name.py")
# module = importlib.util.module_from_spec(spec)
# spec.loader.exec_module(module)

funcs = []
for name, value in vars(module).items():
    if name.startswith("_") or not callable(value):
        continue
    doc = inspect.getdoc(value)
    code = marshal.dumps(value.__code__)
    funcs.append({"name": name, "docstring": doc, "body": code})

for value in funcs:
    name = value["name"]
    doc = value["docstring"]
    code = value["body"]
    # invoke all the functions
    func = types.FunctionType(marshal.loads(code), globals(), name)
    func.__doc__ = doc
    func()

相关问题 更多 >