在 lambda 中访问函数的 __doc__

Accessing __doc__ of function inside a lambda

我想提取一个函数的文档字符串,一旦它被包装在 lambda

考虑以下示例:

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

我得到:

a 1 Foo docstring
b 2 None

如何引用“调用”lambda 时调用的函数?

更新

感谢所有回答:

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

没有“好的”方法来做到这一点。但是,使用 inspect 模块在技术上是可行的。这是一个非常脆弱的实现,适合您获取 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

正如我所说,这个实现是脆弱的。例如,如果调用的第一个函数不在 globals 中,它会立即中断。但是,如果您发现自己处于非常困难的境地,您可能可以为您的用例拼凑出一个解决方案。

但是,如果可能的话,您应该改用 functools

import functools

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

b = functools.partial(foo, 2)

print(b.func.__doc__)

好吧,尝试向 smt 添加文档等功能,根据定义应该是“匿名的”...只能通过一种棘手的方式来完成。 我可以使用嵌套装饰器来实现。

首先注意print('__doc__' in dir(lambda: ''))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__)

输出

>>>  a 1 Foo docstring
>>>  b 2 Foo docstring   # lambda docs # case custom docs

备注:

  1. add_doc_to_lambda 可调用
print(dct['b'])
<function dec.<locals>.<lambda> at 0x7f6eda92ee50>
  1. 还有更直接的方式如
l = lambda: foo(2)
l.__doc__ = foo.__doc__
print(l.__doc__)

可行,但将 lambda 函数赋值给变量是一种不好的做法。

我建议您使用不同的机制 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)),
}