我正在研究装饰这个类的装饰器。它对于实例方法来说很好,但是为类方法提供了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,它的效果非常好。
知道我在这里缺少什么吗?
答案 0 :(得分:1)
如果添加显示的行,它将起作用。这是因为在类定义中应用的@classmethod装饰器改变了getattr(cls, attribute)
返回的内容 - 它将是命名方法的描述符,它添加cls
参数然后调用实际方法。
您需要做的是检索" raw"属性的值,它只是一个常规函数,然后通过显式调用classmethod
将其转换为类方法。这需要" raw" value存储在与相同属性名称关联的类字典__dict__
中,因此需要添加value = cls.__dict__[attribute].__func__
行。
正确处理静态方法也需要类似的东西。 this answer问题Decorating a method that's already a classmethod?中描述了如何对所有不同类型的方法执行此操作。其他一些答案也比我在此更详细地描述了所发生的事情。 / p>
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