简介
看到装饰会想到啥,蛋糕上的裱花?礼物盒上的蝴蝶结?还是礼帽上的羽毛?
这些小东西并不是必要的,蛋糕可以没有裱花,礼物盒可以没有蝴蝶结,礼帽可以没有羽毛。
但是添加了这些小东西,却可以让同一个物品展现出不同的模样。不同的裱花可以做出不同样式的蛋糕,不同的蝴蝶结也可以让礼物盒展现出不同的样式,不同的羽毛可以让礼帽表达不同的意思。
装饰器模式的设计理念就是为已经存在的对象添加装饰
正文
装饰器模式
看过简介的描述,我们可以知道,装饰器模式和类的继承是不一样的,因为装饰器具有以下几点特点:
- 在不改变原有对象的条件下,为对象添加新的“样式”
- 即插即用
下面我们先用一个小例子,演示一下
class Coke:
name = "coke"
price = 3
def get_name(self):
print(self.name)
def get_price(self):
print(self.price,"元")
class Ice:
name = "ice"
price = "0.3"
def __init__(self,drink):
self.drink = drink
def get_name(self):
self.drink.get_name()
print("加",self.name)
def get_price(self):
self.drink.get_price()
print("加",self.price,"元")
c = Coke()
c.get_name()
c.get_price()
i = Ice(c)
i.get_name()
i.get_price()
从上面的例子我们可以看出,不加冰并不影响可乐本身的价格,加冰之后可乐发生了改变。
Python装饰器
说到装饰器,我们不得不聊一下Python装饰器
Python的装饰器采用的就是我们的装饰器设计模式,那么,我们首先先来看一下Python的装饰器是怎么实现的。
函数装饰器
# 写法一
def ice(func):
def wapper(*args):
print("加冰")
return func(*args)
return wapper
class Coke:
name = "coke"
price = 3
@ice
def get_name(self):
print(self.name)
def get_price(self):
print(self.price,"元")
c = Coke()
c.get_name()
c.get_price()
# 写法二
def ice(func):
def wapper(*args):
print("加冰")
return func(*args)
return wapper()
class Coke:
name = "coke"
price = 3
def get_name(self):
print(self.name)
def get_price(self):
print(self.price,"元")
c = Coke()
ice(c.get_name)
c.get_price()
类装饰器
class Ice():
def __init__(self) -> None:
self.name = "ice"
def __call__(self,func):
print(func)
def wapper(*args):
print("加",self.name)
return func(*args)
return wapper
class Coke:
name = "coke"
price = 3
@Ice()
def get_name(self):
print(self.name)
def get_price(self):
print(self.price,"元")
c = Coke()
c.get_name()
c.get_price()
知识点一:@写法的作用
两种装饰器的写法都体现了一样的功能,和我们之前的装饰器模式不一样的地方是,装饰器模式需要将对象传入装饰器类中,和Python的函数装饰器写法二类似。
而Python的装饰器在调用的时候看起来好像是不需要将对象传入的。但是实际上我们在编写Python装饰器的时候,都会发现虽然在调用的时候没有传入对象,但是我们依旧会接受一个func的参数,并且输出在我们输出func和*args的时候会发现,其实func就是被装饰的对象,*args这个元祖只有一个元素,就是Coke的实例。
这就是@写法的作用,将@后面的对象添加()(type方法,涉及到元类)并且将@下面表示的方法作为参数传入被type()创建的对象中。
知识点二:__call__()方法的作用
将实例转化为可调用的对象,例子如下:
class CallTest:
def __init__(self) -> None:
pass
def __call__(self, func):
func()
def testdef():
print("这是一个测试")
ct = CallTest()
ct(testdef)
让实例也可以像类一样被调用,Python中装饰器的类对象写法,就是通过__call__ 方法,将实例变成可以调用的对象,然后将被标记的函数当做参数传入。
知识点三:闭包
当函数接收到传入的参数时,会在函数内部在定义一个函数(套娃),并且在内部定义的函数调用传进来的函数(给传入的func对象添加(),从访问函数地址,变成调用函数)。这种写法叫做闭包。
闭包的好处有可以隔离作用域,每个闭包都有属于自己的作用域。
总结
装饰器本质就是在不影响礼物本质的前提上,为礼物添加装饰,使得原有的礼物添加了新的样式。
在很多情况下都可以得到应用,比如说log记录,登录认证,添加钩子函数等等。