一篇学会Python函数装饰器基础知识

开发 后端
函数装饰器是Python语言最优秀的设计之一,它以非常简洁的方式增强了函数的行为,让崎岖不平之路变得平坦顺畅。

[[401826]]

本文转载自微信公众号「dongfanger」,作者dongfanger。转载本文请联系dongfanger公众号。

函数装饰器是Python语言最优秀的设计之一,它以非常简洁的方式增强了函数的行为,让崎岖不平之路变得平坦顺畅。

函数装饰器是什么

函数装饰器是一个可调用对象,它的参数是另外一个函数。比如:

  1. @decorate 
  2. def target(): 
  3.     print("running target()"

跟下面代码效果是一样的:

  1. def target(): 
  2.     print("running target()"
  3.  
  4. target = decorate(target) 

简单实现@decorate:

  1. def decorate(func): 
  2.     def inner(): 
  3.         print("running inner()"
  4.     return inner 

测试一下:

  1. >>> target() 
  2. running inner() 
  3. >>> target 
  4. <function decorate.<locals>.inner at 0x04899D18> 

新的target是decorate(target)返回的inner函数。

因为装饰器只是代码优化的一种手段,不像if语句for语句那样,决定了程序流程,所以严格来说,装饰器只是语法糖。它有两个特性,一是能把被装饰的函数替换成其他函数,二是装饰器在加载模块时立即执行。

装饰器在导入时执行

若想真正理解装饰器,需要区分导入时和运行时。函数装饰器在导入模块时立即执行,而被装饰的函数只在明确调用时运行。

接下来通过示例对这个特性进行说明,新建registration.py模块:

  1. registry = [] 
  2.  
  3.  
  4. def register(func): 
  5.     # 装饰器函数也可以不定义内部函数 
  6.     print("running register(%s)" % func) 
  7.     registry.append(func) 
  8.     return func 
  9.  
  10.  
  11. @register 
  12. def f1(): 
  13.     print("running f1()"
  14.  
  15.  
  16. @register 
  17. def f2(): 
  18.     print("running f2()"
  19.  
  20.  
  21. def f3(): 
  22.     print("running f3()"
  23.  
  24.  
  25. def main(): 
  26.     print("running main()"
  27.     print("registry ->", registry) 
  28.     f1() 
  29.     f2() 
  30.     f3() 
  31.  
  32.  
  33. if __name__ == "__main__"
  34.     main() 

从结果能看出来:

  • @register作用到f1和f2上,在导入时,在main()调用前就执行了。
  • f3没有装饰器,就没有在main()调用前执行@register。
  • 在main()调用后,明确调用f1()、f2()、f3()才执行函数。

import模块能看得更明显:

  1. >>> import registration 
  2. running register(<function f1 at 0x0189A730>) 
  3. running register(<function f2 at 0x0189A6E8>) 

装饰器在导入时就执行了。

使用装饰器改进策略模式

在《Python设计模式知多少》文章中提到了装饰器可以更优雅的实现策略模式的最佳策略,它的实现代码如下:

  1. promos = [] 
  2.  
  3. def promotion(promo_func): 
  4.     promos.append(promo_func) 
  5.     return promo_func 
  6.  
  7. @promotion 
  8. def fidelity(order): 
  9.     """5% discount for customers with 1000 or more fidelity points""" 
  10.     return order.total() * .05 if order.customer.fidelity >= 1000 else 0 
  11.  
  12. @promotion 
  13. def bulk_item(order): 
  14.     """10% discount for each LineItem with 20 or more units""" 
  15.     discount = 0 
  16.     for item in order.cart: 
  17.         if item.quantity >= 20: 
  18.             discount += item.total() * .1 
  19.     return discount 
  20.  
  21. @promotion 
  22. def large_order(order): 
  23.     """7% discount for orders with 10 or more distinct items""" 
  24.     distinct_items = {item.product for item in order.cart} 
  25.     if len(distinct_items) >= 10: 
  26.         return order.total() * .07 
  27.     return 0 
  28.  
  29. def best_promo(order): 
  30.     """Select best discount available 
  31.     ""
  32.     return max(promo(orderfor promo in promos) 

它解决了"如果想要添加新的促销策略,那么要定义相应函数并添加到promos列表中"这个缺陷,并有更多优点:

  • 新的促销策略,用@promotion装饰器即可添加。
  • 促销策略函数不用以_promo结尾,可以任意命令。
  • 促销策略可以在任意模块定义,只需要使用@promotion装饰器即可。

小结

本文首先介绍了函数装饰器是一个可调用对象,它的参数是另外一个函数。严格来说,它只是语法糖。要理解装饰器,需要区别导入时和运行时,装饰器在导入时就会执行。最后使用装饰器对策略模式的最佳策略进行了优化。为了进一步学习函数装饰器,得先明白另外一个很重要的概念:闭包。

参考资料:

 

《流畅的Python》

 

责任编辑:武晓燕 来源: dongfanger
相关推荐

2019-07-18 16:32:06

Python函数数据

2018-02-01 14:15:00

Python函数

2022-04-12 08:30:52

回调函数代码调试

2017-03-13 09:50:46

Python装饰器

2021-07-01 10:01:16

JavaLinkedList集合

2019-05-09 15:12:20

Linux 系统 数据

2022-09-23 07:15:22

docker网络Liunx

2021-03-13 10:14:59

Python定义函数Python基础

2021-12-01 11:33:21

函数Min

2022-03-10 16:51:46

C语言代码if语句

2022-01-02 08:43:46

Python

2022-03-30 08:37:32

Python函数编程自定义函数

2021-10-18 10:54:48

.NET内存管理

2022-02-07 11:01:23

ZooKeeper

2021-07-06 08:59:18

抽象工厂模式

2021-05-11 08:54:59

建造者模式设计

2021-07-05 22:11:38

MySQL体系架构

2022-08-26 09:29:01

Kubernetes策略Master

2023-01-03 08:31:54

Spring读取器配置

2021-07-02 09:45:29

MySQL InnoDB数据
点赞
收藏

51CTO技术栈公众号