装饰方法以获得不同方法的签名

时间:2017-03-08 17:01:49

标签: python decorator

我有以下课程:

class A(object):

    @classmethod
    def result(cls):
        raise NotImplementedError

    @classmethod
    def square(cls, **kwargs):
        r = cls.result(**kwargs)
        return r ** 2    

class B(A):

    @classmethod
    def result(cls):
        return 2

class C(A):

    @classmethod
    def result(cls, *, x, y):
        return x + y

A类中的方法square尚未了解result的实现,因此通用**kwargs接受任何内容并将其传递给result

我想以一种方式包装square,它采用子实现的result函数签名。因此,当我检查时

inspect.getfullargspec(C.square)
inspect.getfullargspec(C.result)
# both return FullArgSpec(args=['cls'], varargs=None, varkw=None, defaults=None, kwonlyargs=['x', 'y'], kwonlydefaults=None, annotations={})

最好的方法是什么?

functools.wraps正在为名称和文档执行此操作,但不对签名执行此操作。 boltons.funcutils.wraps为名称,文档和签名执行此操作。我想做签名,但不是名字和文件。此外,在我的特定用例中,方法是类方法。

编辑:

我已经使用boltons.funcutils.wraps和MetaClasses为类和实例提供了一个包装工具:

from boltons.funcutils import wraps
# from functools import wraps
from inspect import getfullargspec

def wrap_if_result_signature_desired(self, super, item):
    if item in ['square']:
        return self._signature_wrapper(super.__getattribute__(item))
    return super.__getattribute__(item)

class WrappingMetaClass(type):
    def __getattribute__(self, item):
        return wrap_if_result_signature_desired(self, super(), item)

class A(object, metaclass=WrappingMetaClass):
    def __getattribute__(self, item):        
        return wrap_if_result_signature_desired(self, super(), item)

    @classmethod
    def _signature_wrapper(cls, f):

        @wraps(cls.result)
        def wrapper(*args, **kwargs):
            return f(*args, **kwargs)

        wrapper.__name__ = f.__name__
        wrapper.__doc__ = f.__doc__

        return wrapper


    @classmethod
    def result(cls):
        raise NotImplementedError

    @classmethod    
    def square(cls, **kwargs):
        r = cls.result(**kwargs)
        return r ** 2   


class B(A):    

    @classmethod
    def result(cls):
        return 2

class C(A):

    @classmethod
    def result(cls, *, x, y):
        return x + y


print(getfullargspec(C.square))
print(getfullargspec(C().square))   


# FullArgSpec(args=['cls'], varargs=None, varkw=None, defaults=None, kwonlyargs=['x', 'y'], kwonlydefaults=None, annotations={})
# FullArgSpec(args=['cls'], varargs=None, varkw=None, defaults=None, kwonlyargs=['x', 'y'], kwonlydefaults=None, annotations={})

但是,由于boltons.funcutils.wraps返回函数而不是绑定方法,因此方法本身不起作用。因此:

#Both do not work
print( C.square(x=1,y=2) )
print( C().square(x=1,y=2) )

#     print( C.square(x=1,y=2) )
# TypeError: result() missing 1 required positional argument: 'cls'

注意:在代码运行的意义上使用functools.wraps工作(显然functools.wraps确实将它绑定到类),然而,它并没有接管签名,这是整个目的首先是这项练习。

1 个答案:

答案 0 :(得分:0)

我提出了这个解决方案:

Param(
     [alias('foop')]
     [string]$MyWord = 'hi'
)

function myfunc ([string] $MyWord){
    Write-Host "$MyWord" 
}

# Add all unbound parameters that have default values.
$boundAndDefaultValueParams = $PSBoundParameters
foreach($paramName in $MyInvocation.MyCommand.Parameters.Keys) {
  if (-not $boundAndDefaultValueParams.ContainsKey($paramName)) {
    $val = Get-Variable $paramName -ValueOnly
    if ($null -ne $val) { $boundAndDefaultValueParams.Add($paramName, $val) }
  }
}

myfunc @boundAndDefaultValueParams

输出:

import inspect

def inspect_result_signature(f):
    def inner(cls, *args, **kwargs):
        signature = inspect.getargspec(cls.result)
        print cls.__name__, ':', signature
        return f(cls, *args, **kwargs)
    return inner

class A(object):

    @classmethod
    def result(cls):
        raise NotImplementedError

    @classmethod
    @inspect_result_signature
    def square(cls, *args, **kwargs):
        r = cls.result(*args, **kwargs)
        return r ** 2

class B(A):

    @classmethod
    def result(cls):
        return 2

class C(A):

    @classmethod
    def result(cls, x, y):
        return x + y

print B.square()
print C.square(2, 2)

对于Python3,您必须使用$ python signature.py <class '__main__.B'> () {} B : ArgSpec(args=['cls'], varargs=None, keywords=None, defaults=None) 4 <class '__main__.C'> (2, 2) {} C : ArgSpec(args=['cls', 'x', 'y'], varargs=None, keywords=None, defaults=None) 16 inspect.getfullargspec