Search code examples
pythonpython-3.xlambdadocstring

Accessing __doc__ of function inside a lambda


I would like to extract the docstring of a function once it has been wrapped in lambda.

Consider the following example:

def foo(a=1):
    """Foo docstring"""
    return a

dct = {
    "a": foo,
    "b": lambda: foo(2),
}

for k, v in dct.items()
    print(k, v(), v.__doc__)

I get:

a 1 Foo docstring
b 2 None

How can I reference the function called on "calling" the lambda one?

Update

Thanks for all answers:

from functools import partial

def foo(a=1):
    """Foo docstring"""
    return a

dct = {
    "a": foo,
    "b": partial(foo, 2),
}

for k, v in dct.items():
    if hasattr(v, "func"):
        print(k, v(), v.func.__doc__)
    else:
        print(k, v(), v.__doc__)
a 1 Foo docstring
b 2 Foo docstring

Solution

  • There is no "good" way to do this. However, it is technically possible using the inspect module. Here is a very brittle and fragile implementation that fits your use case of getting the docstring of the first function called by a lambda:

    import inspect
    import re
    
    def get_docstring_from_first_called_function(func):
        # the inspect module can get the source code
        func_source = inspect.getsource(func)
    
        # very silly regex that gets the name of the first function
        name_of_first_called_function = re.findall(r'\w+|\W+', func_source.split("(")[0])[-1]
    
        # if the function is defined at the top level, it will be in `globals()`
        first_called_function = globals()[name_of_first_called_function]
        return first_called_function.__doc__
    
    
    def foo(a=1):
        """Foo docstring"""
        return a
    
    b = lambda: foo(2)
    
    print(get_docstring_from_first_called_function(b))
    > Foo docstring
    

    As I said, this implementation is fragile and brittle. For instance, it breaks instantly if the first function called is not in globals. But if you find yourself in very dire straits, you can probably hack together a solution for your use case.

    If at all possible, however, you should use functools instead

    import functools
    
    def foo(a=1):
        """Foo docstring"""
        return a
    
    b = functools.partial(foo, 2)
    
    print(b.func.__doc__)