有什么方法可以判断函数对象是 lambda 还是 def?

Is there any way to tell if a function object was a lambda or a def?

考虑以下两个函数:

def f1():
    return "potato"

f2 = lambda: "potato"
f2.__name__ = f2.__qualname__ = "f2"

如果不检查原始源代码,是否有任何方法可以检测到 f1 是 def 而 f2 是 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.FunctionTypetypes.CodeType 创建的函数或代码对象。我没有看到任何处理 __code__ 重新分配或 manually-created 函数和代码对象的好方法。

这里

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

您可以使用 ast.NodeVisitor to achieve your goal without hardcoding any calls to the functions by operating on the sources layer, with it you can identify ALL 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