1. 装饰器作用
装饰模式有很多经典的使用场景,例如插入日志、性能测试、事务处理等等,有了装饰器,就可以提取大量函数中与本身功能无关的类似代码,从而达到代码重用的目的,说白了所谓装饰器,就是在不影响函数本身的情况下,为函数增加其他的附带功能,用于方便记录信息以及其他
2. 一个实例
某程序员接到要优化之前做的项目,首先他需要计算出各个模块的运行时间。现在有两种方案,一个是把程序执行时间功能写到模块内部,另一个则是另外写一个函数,但是修改模块内部是程序员大忌,所以采取第二种方案
运行结果:
补充:
程序最后一行是将 函数作为参数传递给 函数
因为函数也是一个对象(或者叫做是一个指针,就是内存地址,C 程序员应该能理解),对象可以被赋值给变量,所以,通过变量也能调用该函数,也直接传递给其他函数(将 main 函数地址传给 program_times)
函数对象有一个 属性,可以拿到函数的名字,其实就是将 main 的内存地址传给 f,f 就是 main() 函数,只不过是指向内存地址相同,名字不同罢了
3. 优化实例
上面的做法有一个问题,就是所有的 调用处都要改为 ,下面做一些改动来避免计时功能对 函数调用代码的影响
运行结果:
补充:
语句 是将 main 作为参数传递给 deco 函数,deco 函数会将 program_times 函数返回,然后在将 program_times 函数赋值给 main。此时的 main 函数其实已经是 program_times 函数了
4. 装饰器(不带参数)
上面的实例其实已经是装饰器了,但是在 python 中,我们可以使用 语法糖来精简装饰器代码
运行结果:
补充:
通过装饰器,我们可以在需要装饰器的函数上方加上 就可以,然后正常调用 mian 函数了
程序中 和 是完全等价的,本质是一样的
语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会
5. 装饰器(带参数)
因为装饰器本质也是函数,是函数就有参数,接下来看一个有参数的装饰器
运行结果:
补充:
从例子中可以看到,对于被装饰函数需要支持参数的情况,我们只要使装饰器的内嵌函数支持同样的签名即可
例子中 main(1, 2) 其实是 deco(main(1, 2)) 的形式,先把 main 作为参数传给 deco 返回一个 program_times,之后再给 program_times 赋值
5. 装饰器(可变参数)
上面例子还有两个问题:
1. 如果多个函数拥有不同的参数形式,怎么共用同样的装饰器?
在 Python 中,函数可以支持 可变参数,所以装饰器可以通过可变参数形式来实现内嵌函数的签名
2. 如果装饰器本身需要支持参数,怎么实现?
如果装饰器本身需要支持参数,那么装饰器就需要多一层的内嵌函数
运行结果:
补充:
例子中 main(1, 2, 3) 其实是 deco("这个是 deco 的参数")(main(1, 2,3)) 的形式,首先执行 deco("这个是 deco 的参数") 返回的是 _deco 函数,再调用返回的 _deco 函数,参数是 main 函数,返回最终的 program_times 函数
6. 装饰器调用顺序
装饰器是可以叠加使用的,那么这是就涉及到装饰器调用顺序了。对于 Python 中的 语法糖,装饰器的调用顺序与使用 语法糖声明的顺序相反
运行结果:
补充:
例子中 main(1, 2) 其实是 deco1(deco2(main(1, 2))) 的形式,所以先执行 @deco_2
7. 内置装饰器在 Python 中有三个内置的装饰器,都是跟 class 相关的: 、 、
staticmethod 是类静态方法,其跟成员方法的区别是没有 self 参数,并且可以在类不进行实例化的情况下调用
classmethod 与成员方法的区别在于所接收的第一个参数不是 self (类实例的指针),而是cls(当前类的具体类型)
property 是属性的意思,表示可以通过通过类实例直接访问的信息
对于 staticmethod 和 classmethod 这里就不介绍了,通过一个例子看看 property
运行结果:
补充:
注意,对于 Python 新式类(new-style class),如果将上面的 装饰器所装饰的成员函数去掉,则 属性为只读属性,使用 进行赋值时会抛出异常。但是,对于 ,所声明的属性不是 的,所以即使去掉 装饰器也不会报错。
领取专属 10元无门槛券
私享最新 技术干货