【问题标题】:Copy signature, forward all arguments from wrapper function复制签名,从包装函数转发所有参数
【发布时间】:2017-02-23 16:09:37
【问题描述】:

我在一个类中有两个函数,plot()show()show(),作为一种方便的方法,除了在plot()的代码中添加两行类似

def plot(
        self,
        show_this=True,
        show_that=True,
        color='k',
        boundary_color=None,
        other_color=[0.8, 0.8, 0.8],
        show_axes=True
        ):
    # lots of code
    return

def show(
        self,
        show_this=True,
        show_that=True,
        color='k',
        boundary_color=None,
        other_color=[0.8, 0.8, 0.8],
        show_axes=True
        ):
    from matplotlib import pyplot as plt
    self.plot(
        show_this=show_this,
        show_that=show_that,
        color=color,
        boundary_color=boundary_color,
        other_color=other_color,
        show_axes=show_axes
        )
    plt.show()
    return

这一切都有效。

我遇到的问题是,show() 包装器中的代码似乎方式太多。我真正想要的:让show() 具有与plot() 相同的签名和默认参数,并将所有参数转发给它。

有什么提示吗?

【问题讨论】:

  • inspect 模块从 Python 3.3 开始就有一个 Signature 类。
  • Python 2 还是 Python 3?后者复制签名的可能性更大
  • 代码必须与 Python 2 兼容,但我很好奇你提到的可能性。

标签: python function signature


【解决方案1】:

Python 3 提供了使用检查模块实际复制包装函数签名的能力:

def show(self, *args, **kwargs):
    from matplotlib import pyplot as plt
    self.plot(*args, **kwargs)
    plt.show()
show.__signature__ = inspect.signature(plot)

现在,如果您在提供 IDLE 等自动完成功能的 shell 中使用 show,您将看到 show 的正确参数,而不是神秘的 *args, **kwargs

【讨论】:

  • 我发现这种方法有问题。由于某种原因,签名未在show 上强制执行。你可以打电话给show(dummy=1),它不会像你打电话给plot那样抱怨。你知道为什么会这样吗?
  • 为什么很简单,签名只是函数对象的一个​​属性。它不会改变调用函数的行为,它只是为自省提供额外的信息。但是,您可以通过调用 inspect.signature(show).bind(*args, **kwargs) 来检查参数是否与签名匹配。
【解决方案2】:

扩展 Serge 的回答我会提出以下建议:

from inspect import signature

# create a decorator to copy signatures
def copy_signature(source_fct): 
    def copy(target_fct): 
        target_fct.__signature__ = signature(source_fct) 
        return target_fct 
    return copy 

# create a short test function as example
def test(a, /, b, c=2, *, d, e): 
    print(a, b, c, d, e)

# now wrap it
@copy_signature(test)
def forwards(*args, **kwds):
    # assert that arguments are correct
    signature(forwards).bind(*args, **kwds) 
    # now do potentially complicated and obscuring tasks
    print('Just wrapping') 
    test(*args, **kwds)

调用signature(forwards).bind(*args, **kwds) 可确保使用正确的参数调用函数。如果你有复杂的函数,在调用函数之前检查参数可以让错误更清楚。

我故意没有在装饰器中包含检查。否则我们将需要另一个函数调用,这会使调试更加复杂。

【讨论】:

  • 很不错的补充,我会用!
【解决方案3】:

您可以使用参数打包/解包:

def show(self, *args, **kwargs):
    from matplotlib import pyplot as plt
    self.plot(*args, **kwargs)
    plt.show()

【讨论】:

    【解决方案4】:

    基于 DerWeh 的answer(在本页):

    def deco_meta_copy_signature(signature_source: Callable):
        def deco(target: Callable):
            @functools.wraps(target)
            def tgt(*args, **kwargs):
                signature(signature_source).bind(*args, **kwargs)
                return target(*args, **kwargs)
            tgt.__signature__ = signature(signature_source)
            return tgt
        return deco
    

    【讨论】:

      猜你喜欢
      • 2015-08-28
      • 2021-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-12
      • 1970-01-01
      • 2022-01-24
      • 2020-11-19
      相关资源
      最近更新 更多