常见的装饰器一般以函数(方法)的方式实现,根据装饰器的原理,原函数或方法被装饰后能得到一个新的可调用对象,那么其实类也能实现这个功能,只要这个类(实例)是可调用的,即实现了__call__方法。
# demo
class Decorator:
def __init__(self, func):
self._func = func
def __call__(self, name, **kwargs):
# 判断简名字是否存在
print('______Decorator call_______')
if name in ('Ben', 'Mike'):
return self._func(name, **kwargs)
return
# 等同与get_fullname = Decorator(get_fullname)
@Decorator
def get_fullname(name):
# 根据简名获取全名
print('---------test-----------')
return name + ' kely'
if __name__ == '__main__':
print(get_fullname('Lily'))
"""
输出:
______Decorator call_______
None
"""
print(get_fullname('Mike'))
"""
输出:
______Decorator call_______
---------test-----------
Mike kely
"""
看到这个标题可能会有点懵逼,如果不懂什么是描述器可以看我之前写的文章:属性访问一文中有说明。实际上实现了__get__方法的类就是描述器。
这里就是要用描述器来装饰类方法,因为描述器的行为决定了它只能在类中。并且被描述器装饰的方法的行为与常规的方法的行为不同。
class Descriptor:
def __init__(self, func):
print('______Descriptor init_______')
self._func = func
def __get__(self, instance, owner):
print('______Descriptor get_______')
name = self._func(instance)
return name
# return self._func
class Test:
# 等同与name = Descriptor(name)
@Descriptor
def name(self, name=None):
print('______Test name_______')
if name is None:
return 'hello'
return name
if __name__ == '__main__':
t = Test()
print(t.name)
"""
______Descriptor init_______
______Descriptor get_______
______Test name_______
hello
"""
十分关键:这样使用的时候,一定要把原本的函数保存下来self._func = func,在__get__通过self._func(instance)进行访问,并且name从外界来看已经是Test的一个属性,而不是一个方法,这样的实现方式很类似@property装饰器的作用,但功能更强大,更加灵活。
class Descriptor:
...
def __get__(self, instance, owner):
print('______Descriptor get_______')
return self._func
if __name__ == '__main__':
t = Test()
print(t.name(t, 'Ben'))
这样就实现了参数的传递,好奇的人会注意到参数中还包括了实例t,这是因为t.name是Descriptor的self._func属性,而self._func是一开始通过func(看一开始的代码)赋予的值,传递过来的func值并不会记录下实例的对象,所以需要传递实例对象给func.
也许你会发现,行为2的情况实际上有第一种类装饰器就可以很好的解决,用第二种很显得更加变扭,所以只推荐第一种行为的时候使用描述器作为装饰器