2017-05-16 72 views
0

我正在研究裝飾類的裝飾器。它對於實例方法來說很好,但爲類方法提供了一個TypeError。代碼如下:裝飾類方法的錯誤

def deco_method(fn): 
    def wrapper(*arg, **kwarg): 
    """ 
    Function: Wrapper 
    """ 
    print "Calling function {}".format(fn.__name__) 
    print arg, kwarg 
    ret_val = fn(*arg, **kwarg) 
    print "Executed function {}".format(fn.__name__) 
    return ret_val 
    return wrapper 

def clsdeco(cls): 
    attributes = cls.__dict__.keys() 

    for attribute in attributes: 
    # Do not decorate private methods 
    if '__' in attribute: 
     continue 

    # Get the method 
    value = getattr(cls, attribute) 
    if not hasattr(value, '__call__'): 
     continue 

    # CHeck if method is a class method or normal method and decoate accordingly 
    if value.im_self is None:# non class method 
     setattr(cls, attribute, deco_method(value)) 
    elif value.im_self is cls: # CHeck if the method is class method 
     setattr(cls, attribute, classmethod(deco_method(value))) 
    else: 
     assert False 
    return cls # return decorated class 



@clsdeco 
class Person: 
    message = "Hi Man" 
    def __init__(self, first_name, last_name): 
    self.fname = first_name 
    self.lname = last_name 
    self.age = None 

    def get_name(self): 
    print "Name is '{} {}'".format(self.fname, self.lname) 

    @classmethod 
    def greet_person(cls): 
    print cls.message 

p = Person('John', 'snow') 
p.greet_person() 

它給出了一個錯誤:

TypeError: greet_person() takes exactly 1 argument (2 given)

如果我刪除@clsdeco,它完美的罰款。

任何想法我在這裏失蹤?

回答

1

如果添加顯示的行,它將起作用。這是因爲類定義中應用的@classmethod修飾器會更改getattr(cls, attribute)返回的內容 - 它將成爲指定方法的描述符,它將添加cls參數,然後調用真實方法。

您需要做的是檢索屬性的「原始」值,該屬性只是一個常規函數,然後通過明確調用classmethod將其重新轉換爲類方法。這需要將「原始」值存儲在類字典__dict__與相同的屬性名稱相關聯,因此需要添加value = cls.__dict__[attribute].__func__行。

正確處理靜態方法也需要類似的東西。如何爲所有不同類型的方法做到這一點在this answer中對Decorating a method that's already a classmethod?這個問題進行了描述。其他一些答案也描述了比我在這裏更詳細的內容。

def clsdeco(cls): 
    attributes = cls.__dict__.keys() 

    for attribute in attributes: 
    # Do not decorate private methods 
    if '__' in attribute: 
     continue 

    # Get the method 
    value = getattr(cls, attribute) 
    if not hasattr(value, '__call__'): 
     continue 

    # Check if method is a class method or normal method and decoate accordingly 
    if value.im_self is None:# non class method 
     setattr(cls, attribute, deco_method(value)) 
    elif value.im_self is cls: # Check if the method is class method 
     value = cls.__dict__[attribute].__func__      # ADDED 
     setattr(cls, attribute, classmethod(deco_method(value))) 
    else: 
     assert False 

    return cls # return decorated class 
+0

謝謝!我沒有注意到getattr返回描述符。 「value = cls .__ dict __ [attribute] .__ func__」works。或者,我們也可以爲python 2.7創建「value = value.im_func」和「value = value .__ func__」[python 3] – Luminos

+0

@Luminos:在Python 2.6中,他們爲'im_func'創建了'__func__'的alais,這會更便於攜帶 - 但我相信使用'cls .__ dict __ [attribute] .__ func__'已經可以在Python 2和Python 3中運行。 – martineau