这样做逻辑上是没问题的,功能是实现了,但是我们调用的时候不再是调用真正的业务逻辑today函数,而是换成了logging_tool函数,这就破坏了原有的代码结构,为了支持日志功能,原有代码需要大幅修改,那么有没有更好的方式的呢?当然有,答案就是装饰器。
二、开天辟地
一个简单的装饰器
以上也是装饰器的原理!!!
三、Pythonic世界的初探
@语法糖 接触 Python 有一段时间的话,对 @ 符号一定不陌生了,没错 @ 符号就是装饰器的语法糖,它放在函数开始定义的地方,这样就可以省略最后一步再次赋值的操作
有了 @ ,我们就可以省去today = logging_tool(today)这一句了,直接调用 today() 即可得到想要的结果。 不需要对today() 函数做任何修改,只需在定义的地方加上装饰器 ,调用的时候还是和以前一样。 如果我们有其他的类似函数,可以继续调用装饰器来修饰函数 ,而不用重复修改函数或者增加新的封装。这样,提高程序可重复利用性,并增加程序的可读性。
装饰器在 Python 使用之所以如此方便,归因于 Python函数能像普通的对象一样能作为参数传递给其他函数,可以被赋值给其他变量,可以作为返回值,可以被定义在另外一个函数内。
2、让装饰器同时支持带参数或不带参数
如上所示,参数有两种类型,一种是字符串,另一种是可调用的函数类型。因此,通过对参数类型的判断即可实现支持带参数和不带参数的两种情况。
3、类装饰器
装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。
(1)示例一、被装饰函数不带参数
(3)示例三、不依赖初始化函数,单独使用__call__函数实现(体现类装饰器灵活性大、高内聚、封装性高的特点) 实现当一些重要函数执行时,打印日志到一个文件中,同时发送一个通知给用户
进一步扩展,给LogTool创建子类,来添加email的功能:
4、装饰函数 -> 装饰类
(1)函数层面的装饰器很常见,以一个函数作为输入,返回一个新的函数; (2)类层面的装饰其实也类似,已一个类作为输入,返回一个新的类;
例如:给一个已有的类添加长度属性和getter、setter方法
五、上古神器
1、@property -> getter/setter方法
示例:给一个Student添加score属性的getter、setter方法
2、@classmethod、@staticmethod
(1)@classmethod 类方法:定义备选构造器,第一个参数是类本身(参数名不限制,一般用cls) (2)@staticmethod 静态方法:跟类关系紧密的函数
简单原理示例:
3、@functools.wraps
装饰器极大地复用了代码,但它有一个缺点:因为返回的是嵌套的函数对象wrapper,不是原函数,导致原函数的元信息丢失,比如函数的docstring、 name 、参数列表等信息。不过呢,办法总比困难多,我们可以通过@functools.wraps将原函数的元信息拷贝到装饰器里面的func函数中,使得装饰器里面的func和原函数有一样的元信息。
@functools.wraps让我们可以通过属性__wrapped__直接访问被装饰的函数,同时让被装饰函数正确暴露底层的参数签名信息
countdown.__wrapped__(1000) # 访问被装饰的函数print(inspect.signature(countdown)) # 输出被装饰函数的签名信息
4、Easter egg
(1) 定义一个接受参数的包装器
@decorator(x, y, z)def func(a, b):pass
等价于
func = decorator(x, y, z)(func)
即:decorator(x, y, z)的返回结果必须是一个可调用的对象,它接受一个函数作为参数并包装它。
(2)一个函数可以同时定义多个装饰器,比如:
欢迎关注我的博客或者公众号:https://home.cnblogs.com/u/Python1234/ Python学习交流
欢迎加入我的千人交流答疑群:125240963
网友评论