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

后端 未结 3 1311
感动是毒
感动是毒 2021-01-12 10:56

Consider the two functions below:

def f1():
    return \"potato\"

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

Short of

相关标签:
3条回答
  • 2021-01-12 11:23

    You could check the code object's name. Unlike the function's name, the code object's name cannot be reassigned. A lambda's code object's name will still be '<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
    

    It is impossible for a def statement to define a function whose code object's name is '<lambda>'. It is possible to replace a function's code object after creation, but doing so is rare and weird enough that it's probably not worth handling. Similarly, this won't handle functions or code objects created by manually calling types.FunctionType or types.CodeType. I don't see any good way to handle __code__ reassignment or manually-created functions and code objects.

    0 讨论(0)
  • 2021-01-12 11:26

    Here

    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))
    

    output

    False
    True
    
    0 讨论(0)
  • 2021-01-12 11:40

    You could use 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 functions definitions and print out it's location, name, etc. Please see code sample below:

    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)
    
    

    The output for code below is following:

    FunctionDef : f1
    Lambda assignment to: ['f2', 'f3'].
    Lambda , line no: 27
    Lambda assignment to: ['f5'].
    Lambda , line no: 28
    AsyncFunctionDef : f6
    
    0 讨论(0)
提交回复
热议问题