考虑以下两个函数:
def f1():
return "potato"
f2 = lambda: "potato"
f2.__name__ = f2.__qualname__ = "f2"
缺少对原始源代码的反省,有什么方法可以检测到它f1
是一个 deff2
还是一个 lambda?
>>> black_magic(f1)
"def"
>>> black_magic(f2)
"lambda"
考虑以下两个函数:
def f1():
return "potato"
f2 = lambda: "potato"
f2.__name__ = f2.__qualname__ = "f2"
缺少对原始源代码的反省,有什么方法可以检测到它f1
是一个 deff2
还是一个 lambda?
>>> black_magic(f1)
"def"
>>> black_magic(f2)
"lambda"
您可以检查代码对象的名称。与函数的名称不同,代码对象的名称不能重新分配。lambda 的代码对象的名称仍然是'<lambda>'
:
>>> x = lambda: 5
>>> x.__name__ = 'foo'
>>> x.__name__
'foo'
>>> x.__code__.co_name
'<lambda>'
>>> x.__code__.co_name = 'foo'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: readonly attribute
语句不可能def
定义代码对象名称为 的函数'<lambda>'
。可以在创建后替换函数的代码对象,但这样做很少见而且很奇怪,可能不值得处理。同样,这不会处理通过手动调用types.FunctionType
或创建的函数或代码对象types.CodeType
。我看不到任何处理__code__
重新分配或手动创建的函数和代码对象的好方法。
您可以使用ast.NodeVisitor来实现您的目标,而无需通过在源层上操作对函数的任何调用进行硬编码,使用它您可以识别所有 Lambda、FunctionDef、AsyncFunctionDef函数定义并打印出它的位置、名称等。请参阅代码下面的示例:
import ast
class FunctionsVisitor(ast.NodeVisitor):
def visit_Lambda(self, node):
print(type(node).__name__, ', line no:', node.lineno)
def visit_FunctionDef(self, node):
print(type(node).__name__, ':', node.name)
def visit_AsyncFunctionDef(self, node):
print(type(node).__name__, ':', node.name)
def visit_Assign(self, node):
if type(node.value) is ast.Lambda:
print("Lambda assignment to: {}.".format([target.id for target in node.targets]))
self.generic_visit(node)
def visit_ClassDef(self, node):
# Remove that method to analyse functions visitor and functions in other classes.
pass
def f1():
return "potato"
f2 = f3 = lambda: "potato"
f5 = lambda: "potato"
async def f6():
return "potato"
# Actually you can define ast logic in separate file and process sources file in it.
with open(__file__) as sources:
tree = ast.parse(sources.read())
FunctionsVisitor().visit(tree)
以下代码的输出如下:
FunctionDef : f1
Lambda assignment to: ['f2', 'f3'].
Lambda , line no: 27
Lambda assignment to: ['f5'].
Lambda , line no: 28
AsyncFunctionDef : f6
这里
def f1():
return "potato"
f2 = lambda: "potato"
def is_lambda(f):
return '<lambda>' in str(f.__code__)
print(is_lambda(f1))
print(is_lambda(f2))
输出
False
True