2017-03-08 64 views
1

我有以下類別:裝飾方法有不同的方法的簽名

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和元類:

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工作。包裹返回一個函數而不是一個綁定方法。因此:

#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不將其綁定到類),但是,它並沒有接過簽名,這是整個目的這個練習的第一位。

+0

我所知,沒有以編程方式創建一個功能Python在運行時定義的簽名。除了使用'compile'或直接的AST操作,當然,如果你出於某種原因希望你的裝飾函數具有特定的簽名,這可能是一種有效的方法。你爲什麼需要它?如果你更詳細地解釋了更大的問題,那麼幫助會更容易。 – 9000

+0

只是回答了一個裝飾者的方法問。 –

+0

@ 9000基類實現不同模型的共同特徵,其中每個模型以不同的方式實現「結果」方法。我們知道共同特徵需要與'結果'方法相同的參數。如果您認爲不同的代碼設計更合適,我絕對有興趣。 – RickB

回答

0

我想出了這個解決方案:

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) 

輸出:

$ 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 

對於Python3,你必須使用inspect.getfullargspecinspect.signature

+0

也許我不清楚,這並不能解決我的問題。調用'inspect.getfullargspec(C.square)'返回'kwonlyargs = []',而我希望它是'kwonlyargs = ['x','y']' – RickB