【问题标题】:Accessing __doc__ of function inside a lambda在 lambda 中访问函数的 __doc__
【发布时间】:2021-12-06 13:42:50
【问题描述】:

我想提取一个函数的文档字符串,一旦它被包裹在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

【问题讨论】:

  • 这是一个有趣的问题。我不确定这是否可能,因为 'b' 是一个完全没有文档字符串的全新函数。我不确定您是否可以内省函数调用的函数。
  • 你不能,你最好使用functools.partial,它将包装函数保留为func属性-functools.partial(foo, 2).func.__doc__
  • 你不能。一个函数可以调用多个函数,那么 python 应该如何知道哪个“子”文档字符串是正确的呢?我能想到的最好的方法是在 lambda 上使用functools.wraps,它将包装函数的文档字符串复制到包装函数。
  • 始终可以选择“手动”将文档字符串添加到您的 lambda,例如 dct["b"].__doc__ = "Lambda docstring",但我不知道这是否适合您的实际用例。

标签: python python-3.x lambda docstring


【解决方案1】:

没有“好的”方法可以做到这一点。但是,使用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__)

【讨论】:

    【解决方案2】:

    好吧,尝试向 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 函数分配给变量是一种不好的做法。

    【讨论】:

      【解决方案3】:

      我建议在这里为您的lambda 使用不同的机制可能是合适的。任何返回正确包装的FunctionType 的东西都可以直接访问正确的__doc__。一种常见的方法是将functools.partialfunctools.wraps 一起使用:

      from functools import partial, wraps
      
      dct = {
          "a": foo,
          "b": wraps(foo)(partial(foo, 2)),
      }
      

      【讨论】:

        猜你喜欢
        • 2017-10-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-01
        • 2018-06-13
        相关资源
        最近更新 更多