代码之家  ›  专栏  ›  技术社区  ›  alex

在方法中复制函数签名

  •  0
  • alex  · 技术社区  · 5 年前

    我正在尝试创建一个函数 Bar.bar 签名并复印文件( OP )来自 foo :

    from inspect import signature
    import functools
    from typing import Callable
    
    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)
            tgt.__doc__ = signature_source.__doc__
            print('Signature 1)', signature(tgt))
            return tgt
        return deco
    
    def foo(a, b, c=0, d=1, **kwargs):
        """ foo! """
        pass
    
    class Bar:
        @deco_meta_copy_signature(foo)
        def bar(self):
            pass
    
    b = Bar()
    print('Signature 2)', signature(b.bar))
    

    其中打印:

    Signature 1) (a, b, c=0, d=1, **kwargs)
    Signature 2) (b, c=0, d=1, **kwargs)
    

    正如你在书中看到的 签名2 第一个参数不被保留。我假设这与目标函数是方法和接收有关 self 参数我怎样才能补充呢 自己 在拥有所有其他参数的同时?

    0 回复  |  直到 5 年前
        1
  •  1
  •   Booboo    5 年前

    下面的方法可以工作,但它是通过更新“private”成员来实现的, _parameters ,当然 Signature 这是我对未来的免责声明。

    装饰程序查看目标的签名,以及是否有名为 self ,则如果复制源的签名并在前面加上附加参数:

    from inspect import signature, Parameter
    import functools
    from typing import Callable
    from types import MappingProxyType
    from collections import OrderedDict
    from copy import deepcopy
    
    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)
            sig_s = signature(signature_source)
            sig_t = signature(target)
            parameters = sig_t.parameters
            if len(parameters):
                # get first parameter
                it = iter(parameters)
                param0 = next(it)
                if param0 == 'self':
                    #add extra dummy parameter that will be discarded:
                    d = OrderedDict()
                    d['dummy'] = Parameter('dummy', Parameter.POSITIONAL_OR_KEYWORD)
                    d['self'] = Parameter('self', Parameter.POSITIONAL_OR_KEYWORD)
                    for k, v in sig_s.parameters.items():
                        d[k] = v
                    sig_s = deepcopy(sig_s) # leave actual signature unmodified
                    sig_s._parameters = MappingProxyType(d) # accessing "private" member: beware!
            tgt.__signature__ = sig_s
            tgt.__doc__ = signature_source.__doc__
            print('Signature 1)', signature(tgt))
            return tgt
        return deco
    
    def foo(a, b, c=0, d=1, **kwargs):
        """ foo! """
        pass
    
    class Action:
        def doit(self, x=9):
            pass
    
    edit = Action()
    
    class Bar:
        @deco_meta_copy_signature(foo)
        def bar(self):
            pass
    
        @deco_meta_copy_signature(edit.doit)
        def foo(self):
            pass
    
    
    @deco_meta_copy_signature(foo)
    def bar(x=8):
        pass
    
    @deco_meta_copy_signature(edit.doit)
    def foo(x=8):
        pass
    
    b = Bar()
    print('Signature 2)', signature(b.bar))
    print('Signature 3)', signature(b.foo))
    print('Signature 4)', signature(bar))
    print('Signature 5)', signature(foo))
    

    印刷品:

    Signature 1) (dummy, self, a, b, c=0, d=1, **kwargs)
    Signature 1) (dummy, self, x=9)
    Signature 1) (a, b, c=0, d=1, **kwargs)
    Signature 1) (x=9)
    Signature 2) (self, a, b, c=0, d=1, **kwargs)
    Signature 3) (self, x=9)
    Signature 4) (a, b, c=0, d=1, **kwargs)
    Signature 5) (x=9)