前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >搞定三大神器之 Python 装饰器

搞定三大神器之 Python 装饰器

作者头像
double
发布2020-09-15 10:30:41
4820
发布2020-09-15 10:30:41
举报
文章被收录于专栏:算法channel算法channel

学会 Python 装饰器

装饰器,几乎各大Python框架中都能看到它的身影,足以表明它的价值!它有动态改变函数或类功能的魔力!

本专题的目录:

  • 学会 Python 装饰器
    • 1 什么是装饰器
    • 2 装饰器的结构
    • 3 为什么要这样
    • 4 装饰一个函数
    • 5 装饰一个类
    • 6 装饰器层叠
    • 7 温馨提醒
    • 总结

1 什么是装饰器

对于受到封装的原函数比如f来说,装饰器能够在f函数执行前或者执行后分别运行一些代码。

2 装饰器的结构

装饰器也是一个函数,它装饰原函数f或类cls后,再返回一个函数g

装饰一个函数:

代码语言:javascript
复制
def decorator(f):
  # 定义要返回的函数
  def g():
    print('函数f执行前的动作')
    f()
    print('函数f执行后的动作')
  return g

装饰一个类:

代码语言:javascript
复制
def decorator(cls):
  # 定义要返回的函数
  def g():
    print('类cls执行前的动作')
    f()
    print('类cls执行后的动作')
  return g

使用装饰器很简单,@+自定义装饰器 装饰要想装饰的函数。

3 为什么要这样

要想理解装饰器为什么要有这种结构,要首先想明白装饰器的目标是什么。

它的价值在于为原函数f增加一些行为,前提必须不能破坏函数f,所以肯定不能改变f的内部结构,所以只能在调用f前后定义一些行为。

同时,装饰器函数decorator返回值又是什么?你可以思考下,返回一个函数是再好不过的了,它包装了原函数f.

4 装饰一个函数

printStar函数接收一个函数f,返回值也是一个函数,所以满足装饰器的结构要求,所以printStar是一个装饰器。

代码语言:javascript
复制
def printStar(f):
    def g():
        print('*'*20)
        f()
        print('*'*20)
    return g

printStar装饰器实现f函数执行前、后各打印20个*字符。

使用printStar:

代码语言:javascript
复制
@printStar
def f():
    print('hello world')

调用:

代码语言:javascript
复制
if __name__ == '__main__':
   ### 改变函数功能
   f()

打印结果:

代码语言:javascript
复制
********************
hello world
********************

可以很方便的装饰要想装饰的其他函数,如下:

代码语言:javascript
复制
@printStar
def g():
    print('welcome to Python')

5 装饰一个类

除了可以装饰函数f外,还可以装饰类cls,两者原理都是一样的。

下面给出一个装饰器实现单例模式的例子,所谓单例就是类只有唯一实例,不能有第二个。

代码语言:javascript
复制
def singleton(cls):
   instance = {}

   def get_instance(*args, **kwargs):
       if cls not in instance:
           instance[cls] = cls(*args, **kwargs)
       return instance[cls]
   return get_instance

定义字典instance,键值对分别为类和实例,这样确保只cls()一次。

使用装饰器singleton修饰类:

代码语言:javascript
复制
@singleton
class CorePoint:
   pass

测试:

代码语言:javascript
复制
if __name__ == '__main__':
   ### 改变类的功能
   c1 = CorePoint()
   c2 = CorePoint()
   print(c1 is c2) # True

6 装饰器层叠

上面原函数f不仅能被一个装饰器修饰,还能被n多个装饰器修饰。

下面再定义一个装饰器printLine,被修饰函数执行前后打印20个 -

代码语言:javascript
复制
def printLine(f):
    def g():
        print('-'*20)
        f()
        print('-'*20)
    return g

使用上文定义好的printStarprintLine同时装饰函数f:

代码语言:javascript
复制
@printStar
@printLine
def f():
    print('hello world')

此时再调用函数f:

代码语言:javascript
复制
if __name__ == '__main__':
   ### 改变函数功能
   f()

打印结果:

代码语言:javascript
复制
********************
--------------------
hello world
--------------------
********************

f被装饰后,先打印*,再打印 -

层叠多一层,原函数f就变强大一层。使用装饰器,还能实现功能抽离,进一步实现松耦合。

7 温馨提醒

打印原函数f的名字__name__,结果为f

代码语言:javascript
复制
In [1]: def f(): 
   ...:     pass 

In [4]: f.__name__                                                              
Out[4]: 'f'

但是,被装饰后函数名字f变为g,这不是我们希望的!

代码语言:javascript
复制
@printStar
def f():
  pass

f()
f.__name__ # g

Python提供的解决方案:使用functools模块中的wraps装饰器:

代码语言:javascript
复制
from functools import wraps

def printStar(f):
    @wraps(f)
    def g():
        print('*'*20)
        f()
        print('*'*20)
    return g

此时再打印被装饰后f的名字,显示f,正常!

总结

  • 学会 Python 装饰器
    • 1 什么是装饰器
    • 2 装饰器的结构
    • 3 为什么要这样
    • 4 装饰一个函数
    • 5 装饰一个类
    • 6 装饰器层叠
    • 7 温馨提醒
    • 总结
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-09-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员郭震zhenguo 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 学会 Python 装饰器
    • 1 什么是装饰器
      • 2 装饰器的结构
        • 3 为什么要这样
          • 4 装饰一个函数
            • 5 装饰一个类
              • 6 装饰器层叠
                • 7 温馨提醒
                  • 总结
                  相关产品与服务
                  日志服务
                  日志服务(Cloud Log Service,CLS)是腾讯云提供的一站式日志服务平台,提供了从日志采集、日志存储到日志检索,图表分析、监控告警、日志投递等多项服务,协助用户通过日志来解决业务运维、服务监控、日志审计等场景问题。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档