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?
CodePudding user response:
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__)
CodePudding user response:
Well, trying to add features such as docs to smt which by definition should be "anonymous"... could be done only in a tricky way. I could achieve that using nested decorators.
Notice first that print('__doc__' in dir(lambda: ''))
is True
.
def add_doc_to_lambda(func, a, docs=''):
# also provide custom docs parameter
if docs == '':
return (lambda l: (lambda _=setattr(l, '__doc__', func.__doc__): l)())(lambda: func(a))
return (lambda l: (lambda _=setattr(l, '__doc__', docs): l)())(lambda: func(a))
def foo(a=1):
"""Foo docstring"""
return a
dct = {
"a": foo,
"b": add_doc_to_lambda(foo, 2), # add_doc_to_lambda(foo, 2, 'lambda docs') # case of custom docs
}
for k, v in dct.items():
print('>>> ', k, v(), v.__doc__)
Output
>>> a 1 Foo docstring
>>> b 2 Foo docstring # lambda docs # case custom docs
Remark:
add_doc_to_lambda
is callable
print(dct['b'])
<function dec.<locals>.<lambda> at 0x7f6eda92ee50>
- Also a more straightforward way such as
l = lambda: foo(2)
l.__doc__ = foo.__doc__
print(l.__doc__)
works but assignment of lambda function to a variable is a bad practice.
CodePudding user response:
I suggest that a different mechanism for your lambda
may be appropriate here. Anything that returns a properly wrapped FunctionType
is going to have direct access to the correct __doc__
. One common method would be to use functools.partial
with functools.wraps
:
from functools import partial, wraps
dct = {
"a": foo,
"b": wraps(foo)(partial(foo, 2)),
}