设为首页 收藏本站
查看: 1021|回复: 0

[经验分享] 对Python中装饰器(Decorator)的理解与进阶

[复制链接]

尚未签到

发表于 2018-8-14 07:29:10 | 显示全部楼层 |阅读模式
  有时候我们项目中的某些功能做些修改即需要对内部的某些函数添加一些附加功能,但是为了安全起见不想改变函数的源代码以及函数的调用方式,那么装饰器在这个地方会给我们带来很大的帮助。
  装饰器(Decorator):(又叫语法糖)
  定义:本质是函数,功能(装饰其它函数)就是为其他函数添加附加功能
  原则:(1).不能修改被装饰的函数的源代码
  (2).不能修改被装饰的函数的调用方式
  1.先来实现一个简单的装饰器示例:
#!/usr/bin/env python  
# -*- coding:utf-8 -*-
  
#定义一个简单的装饰器
  
def simple_wrapper(func):
  
    def wrapper():
  
        print("我是装饰器,我用来装饰%s" % func.__name__)
  
        func()
  
    return wrapper
  
#需要装饰的函数
  
@simple_wrapper
  
def say_hello():
  
    print("Hello World")
  
#执行say_hello()函数
  
say_hello()
  
'''运行结果如下:
  
我是装饰器,我用来装饰say_hello
  
Hello World
  
'''
  上边实现了一个简单的装饰器,能过用来装饰不带参数的函数,通过这个简单的示例,我们大概对装饰器的基本实现有一个大概的了解。但是如果想充分了解并掌握装饰的原理必要还要对python中的高阶函数、嵌套函数、以及函数即“变量”等概念有一定的了解和掌握。下边我会对装饰器以及相关的内容进行举例说明。
  2.上边实现了一个简单的能够装饰不带参数的装饰器,但在正常情况下,我们的函数都需要传入适当的参数,如何能够实现对有参数的方法进行装饰呢?
#!/usr/bin/env python  
# -*- coding:utf-8 -*-
  
def simple_wrapper(func):
  
    def wrapper(*args, **kwargs):
  
        print("我是装饰器,我用来装饰%s方法" % func.__name__)
  
        func(*args, **kwargs)
  
    return  wrapper
  
@simple_wrapper
  
def say_hello(name):
  
    print("Hello",name)
  
#执行say_hello()函数
  
say_hello("Jack")
  
'''运行结果:
  
我是装饰器,我用来装饰say_hello方法
  
Hello Jack
  
'''
  
#是不是同样很简单呢? 这种方式既可以装饰带任意参数的函数,也可以装饰不带参数的函数。
  3.上边的装饰器已经可以实现基本要求,即可以完成对指定的函数添加附加功能的作用,但是在某些特定时候,我们需要装饰器自身也带上参数,如何实现呢?
#!/usr/bin/env python  
# -*- coding:utf-8 -*-
  
def my_wrapper(args):
  
    print("我的参数是:",args)
  
    def simple_wrapper(func):
  
        def wrapper(*args, **kwargs):
  
            print("我是装饰器,我用来装饰%s方法" % func.__name__)
  
            func(*args, **kwargs)
  
        return  wrapper
  
    return simple_wrapper
  

  
@my_wrapper("simple")
  
def say_hello(name):
  
    print("Hello",name)
  
#执行say_hello()函数
  
say_hello("Jack")
  
'''运行结果:
  
我的参数是: simple
  
我是装饰器,我用来装饰say_hello方法
  
Hello Jack
  
'''
  是不是同样的很简单呢?
  简单是简单,但是关键装饰器是怎么实现对其它函数添加附加功能呢?上边的函数是如何运行的呢?下面请看下边的简单示例:
#!/usr/bin/env python  
# -*- coding:utf-8 -*-
  
def simple_wrapper(func):
  
    def wrapper(*args, **kwargs):
  
        print("我是装饰器,我用来装饰%s方法" % func.__name__)
  
        func(*args, **kwargs)
  
    return  wrapper
  

  
def say_hello(name):
  
    print("Hello",name)
  

  
say_hello = simple_wrapper(say_hello)   #这里就等同于@simple_wrapper的作用
  
#其实这里相当于say_hello=wrapper
  
say_hello("Jack")        #所有在执行say_hello("Jack"),就相当调用了wrapper("Jack")函数
  简单解释:可以看到上边也能实现了装饰器的功能,对该示例简要分析:因为在python中函数作为一个对象但也可以看成一种“变量”,不仅可以将函数名拿来赋值给其它变量,也可以将函数名当做参数传递给其它函数,并且还可以将函数名作为返回值。(通俗点可以这样说,就是函数名在内存中就是一个内存地址,它指向函数体的内存地址空间,所以可以将函数名当做一个“变量”来进行相关操作,单当在函数名"变量"后边加上()的时候它就变成了函数调用,会去执行函数体。)在看上边的小例子,当执行say_hello = simple_wrapper(say_hello) 这一步的时候,将函数名say_hello当做参数传给了函数simple_wrapper(),但是接着函数simple_wrapper()将内部函数wrapper作为返回值返回,然后say_hello“被重新复制”,即say_hello指向了wrapper函数体的内存空间,接着当执行say_hello("Jack")的时候就相当于执行wrapper("Jack")。
  4.通过上边的示例我们会发现当使用装饰器的时候,函数say_hello其实被simple_wrapper取代了,理所当然它的__name__等信息都变成了simple_wrapper函数的信息。如何既能让函数被装饰,又不改变其自身的信息呢?其实在Python里提供了一个functools.wraps,而wraps也是一个装饰器,它作用就是实现这种功能的。
#!/usr/bin/env python  
# -*- coding:utf-8 -*-
  
'''
  
def simple_wrapper(func):
  
    def wrapper(*args, **kwargs):
  
        print("我是装饰器,我用来装饰%s方法" % func.__name__)
  
        func(*args, **kwargs)
  
    return  wrapper
  
@simple_wrapper
  
def say_hello(name):
  
    print("Hello",name)
  
#输出say_hello现在的__name___属性
  
print(say_hello.__name__)  #输出结果为:wrapper
  
'''
  
######使用functools.wraps
  
from functools import wraps
  
def simple_wrapper(func):
  
    @wraps(func)
  
    def wrapper(*args, **kwargs):
  
        print("我是装饰器,我用来装饰%s方法" % func.__name__)
  
        func(*args, **kwargs)
  
    return  wrapper
  
@simple_wrapper
  
def say_hello(name):
  
    print("Hello",name)
  
#输出say_hello现在的__name__属性
  
print(say_hello.__name__)  #输出结果为:say_hello
  5.其实一个函数可以同时定义多个装饰器,当一个函数定义多个装饰器时,装饰器的执行顺序是先执行最里层的装饰器最后调用最外层的装饰器,简单示例:
@a  
@b
  
@c
  
def say_hello():
  
   pass
  
#该示例中装饰器的执行顺序是c > b > c(等效于a(b(c(say_hello))))

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-551357-1-1.html 上篇帖子: python笔记day4 下篇帖子: python QQ机器人 (转载)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表