单例类装饰器的实现在文章最后面
1、不带参数的装饰器
我们用一个实际的例子来引入装饰器的概念,比如我们现在有一个方法Method(),然后我们需要在方法Method()执行之前在终端打印"Method is running",最简单的方法是在方法A()的开始部分加上一行代码:print("Method is running")
能不能不修改方法A()的代码实现这个功能呢?答案是肯定的,可以写一个方法decorator(),然后把A()作为入参,代码如下:
def decorator(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
print("Method is running")
return fun()
return wrapper
python中的方法可以作为参数传入另一个方法,所以当我们执行decorator(Method)的时候,返回值实际也是一个“方法”,这个“方法”会先执行print("Method is running")语句,然后再执行Method()。
这样修改后Method()代码确实没有变化,但是客户端的调用代码需要进行修改,之前调用方法Method (),现在是调用decorator(Method)(),为了不影响客户端调用代码,我们使用python的语法糖改造方法Method()的代码。
@decorator
def Method():
# do something
实际上只是在Method()的前面加上一行@decorator,这样客户端代码中调用Method()实际上就相当于调用decorator(Method)()。
装饰器中的@functools.wraps(fun)可以保证返回的方法__name__属性和入参方法fun的__name__属性相同。
2、带入参的装饰器
上面不带参数的装饰器中,我们实现了在执行方法Method()之前打印"Method is running",如果我们想要打印自定义的字符串,就需要使用带参数的装饰器。
实际上就是在不带参数的装饰器上再封装一层即可,代码如下:
def decorator_par(name):
def decorator(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
print("Method {} is running".format(name))
return fun()
return wrapper
return decorator
在使用装饰器的时候设置参数name,实现打印不同的信息。完整的代码如下:
import functools
def decorator_par(name):
def decorator(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
print("Method {} is running".format(name))
return fun()
return wrapper
return decorator
@decorator_par("A")
def A():
print("I am A")
@decorator_par("B")
def B():
print("I am B")
A()
B()
输出为:
Method A is running
I am A
Method B is running
I am B
3、单例类装饰器的实现
在python中使用装饰器来实现一些重复性的工作很方便,例如下面实现的单例装饰器方法,在任何类前面加上@singleton就可以将该类修改成一个单例类,在开发具体的类的时候只需要关注具体类的功能即可,这就是装饰器的便捷之处。
def singleton(cls):
def wrapper(*args, **kwargs):
if not hasattr(cls, "__single_instance"):
setattr(cls, "__single_instance", cls(*args, **kwargs))
wrapper.clean = lambda : delattr(cls, "__single_instance")
return getattr(cls, "__single_instance")
return wrapper
@singleton
class Apple:
def __init__(self, color, price):
self.color = color
self.price = price
apple1 = Apple("red", 1.2)
print(f"apple1 id: {id(apple1)}, color: {apple1.color}, price: {apple1.price}")
apple2 = Apple("green", 0.7)
print(f"apple2 id: {id(apple2)}, color: {apple2.color}, price: {apple2.price}")
Apple.clean()
apple3 = Apple("green", 1.5)
print(f"apple3 id: {id(apple3)}, color: {apple3.color}, price: {apple3.price}")
上面的代码输出如下(python3.6.6):
apple1 id: 2476531484320, color: red, price: 1.2
apple2 id: 2476531484320, color: red, price: 1.2
apple3 id: 2476531484432, color: green, price: 1.5
可以看到前两次实例化的apple1和apple2内存地址是相同的,说明Apple类被singleton装饰后实现了单例,然后调用Apple.clean()清除了缓存的单例对象之后,就可以重新生成一个新的对象apple3。
上面的实现使用动态绑定特性,python中的类实际上也是一个对象,因此可以动态的绑定属性和方法。