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

[经验分享] Python装饰器

[复制链接]
累计签到:224 天
连续签到:4 天
发表于 2019-11-29 09:52:30 | 显示全部楼层 |阅读模式
最近在回看Python的时候,简单的来记一下装饰器的实现
以下操作环境为Python3.6
Python的装饰器英文名叫Decorator,其中组成装饰器的最重要的两个技术:闭包(Closure)以及柯里化(Currying)
为何使用装饰器?
在函数式编程过程中,函数需要增加某项功能时,为了避免硬编码(直接修改原函数),保留原函数的核心功能,将其他非该函数的功能剥离出去,因此衍生出了装饰器
闭包(Closure)
闭包:嵌套函数中,内层函数引用了外层函数的自由变量,就形成了闭包
以上是闭包的定义,在实现闭包前需要知道Python是允许有嵌套函数的,嵌套函数就是Python允许在内部定义函数并将函数返回出去
def outer():
    def inner():
        return 'I am inner'
    return inner

fn = outer() # outer函数返回的是inner,相当于 fn = inner,但是fn仅仅只是记住了inner的函数名(相当于指向了inner函数)
print(fn) # 直接使用fn并不会调用内部的inner
print(fn()) # 这行代码会将内部的inner函数进行调用,打印'I am inner'
闭包:嵌套函数中,内层函数引用了外层函数的自由变量,就形成了闭包
上面代码解释了Python的嵌套函数,接下来在说何为内层函数引用了外层函数的自由变量,在此之前,需要知道Python的作用域,即LEGB原则
LEGB:
Local:本地变量,调用时创建,结束时消亡
Enclosing:嵌套函数外部函数的命名空间(可有可无)
Global:全局变量
Build-in:内建变量,在解释器启动时加载的变量
x = 'global' # x为全局变量

def outer():
    x = 'enclosing' # 内部有inner函数,因此这个就是enclosing,如果内部没有嵌套函数,那么这个就是local变量

    def inner():
        x = 'local' # x为本地变量
        return x

    return inner
闭包:嵌套函数中,内层函数引用了外层函数的自由变量,就形成了闭包
那么内层函数引用了外层函数的自由变量,意思就是内部的local变量调用了外面的变量
def outer():
    x = 1
    def inner():
        return x
    return inner

print(x) # 报错:outer函数未调用,x = 1 未生成
fn = outer()
print(x) # 报错:outer函数调用结束,x = 1 已消亡
print(fn()) # 闭包的特性返回了x变量
print(fn())
print(fn())
print(fn())
以上代码在调用fn()的时候返回了1,这就是闭包:嵌套函数中,内层函数引用了外层函数的自由变量,就形成了闭包
柯里化(Currying)
柯里化:是把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术
定义比较难懂,其实就是 f(x, y) --> f(x)(y)
def foo(x, y):
    return x + y

foo(2, 3)

def foo(x): # 柯里化
    def _foo(y):
        return x + y
    return _foo

foo(2)(3)
改写顺序:foo的形参y去掉,return x + y 用一个函数嵌套(形参为y),然后返回该函数
装饰器(Decorator)实现
有了闭包以及柯里化技术之后就可以来实现Python的装饰器了,下面是演变过程
# 在不改变add函数的基础上,增加打印功能

def add(x, y):
    return x + y

def logger(x, y):
    print('function is {}, x is {}, y is {}'.format(fn.__name__, x, y))
    ret = add(x, y) # 直接调用全局的add函数
    return ret

print(logger(4, 5))
以上代码是在add函数的基础之上添加打印功能
# 将add函数传递到logger函数内部,完成闭包

def add(x, y):
    return x + y

def logger(fn, x, y):
    print('function is {}, x is {}, y is {}'.format(fn.__name__, x, y))
    ret = fn(x, y)
    return ret

print(logger(add, 4, 5)) # 调用了外层的自由变量add函数
add函数被传入至logger函数内部,用到了闭包的技术,logger函数记住了add函数
# 为了能在所有函数中都可以使用到装饰器,那么形参上就必须要做改变

def add(x, y):
    return x + y

def avg(x, y, z):
    return (x + y + z)/3

def foo(x, y, *args, a, b, **kwargs):
    pass

def logger(fn, *args, **kwargs):
    print('function is {}, args is {}, kwargs is {}'.format(fn.__name__, args, kwargs)) # 参数结构
    ret = fn(*args, **kwargs)
    return ret

print(logger(add, 4, 5))
print(logger(avg, 4, 5, 6))
print(logger(foo, 4, 5, a=1, b=2, c=3))
logger函数使用了*args, **kwargs,来收集不同函数的形参
# 将函数柯里化,把内部形参剥离出来,完成装饰器

def add(x, y):
    return x + y

def avg(x, y, z):
    return (x + y + z)/3

def foo(x, y, *args, a, b, **kwargs):
    pass

def logger(fn):
    def inner(*args, **kwargs):
        print('function is {}, args is {}, kwargs is {}'.format(fn.__name__, args, kwargs))   
        ret = fn(*args, **kwargs)
        return ret
    return inner

add = logger(add)
print(add(4, 5))

avg = logger(avg)
print(avg(4, 5, 6))

foo = logger(foo)
print(foo(4, 5, a=1, b=2, c=3))
此操作需要在每个函数定义后,进行重新定义,Python提供了语法糖,利用@关键字来减少代码的冗余
# 利用Python提供的语法糖,减少代码的冗余

def logger(fn):
    def inner(*args, **kwargs):
        print('function is {}, args is {}, kwargs is {}'.format(fn.__name__, args, kwargs))   
        ret = fn(*args, **kwargs)
        return ret
    return inner

@logger # 相当于 add = logger(add)
def add(x, y):
    return x + y

@logger # 相当于 avg = logger(avg)
def avg(x, y, z):
    return (x + y + z)/3

@logger # 相当于 foo = logger(foo)
def foo(x, y, *args, a, b, **kwargs):
    pass


print(add(4, 5))

print(avg(4, 5, 6))

print(foo(4, 5, a=1, b=2, c=3))
需要注意的是logger函数要提前定义



运维网声明 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-989053-1-1.html 上篇帖子: python库myqr生成二维码 下篇帖子: python pip源的配置
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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