打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Python基础之装饰器

装饰器

函数名的运用

关于函数名

函数名是⼀个变量,但它是⼀个特殊的变量。与括号配合可以执⾏函数的变量。

查看函数名的内存地址:

def func():    print('呵呵')print(func)  # <function func at 0x10983c048>

做变量

def func():    print('呵呵')a = func  # 把函数当成变量赋值给另外一个变量a()  # 通过变量a调用函数

做容器的元素

def func1():    print('func1')    def func2():    print('func2')        def func3():    print('func3')        def func4():    print('func4')list1 = [func1, func2, func3, func4]for i in list1:    i()

做参数

def func1():    print('func1')def func2(arg):    print('start')    arg()  # 执行传递进来的arg    print('end')func2(func1)  # 把func1当成参数传递给func2

做返回值

def func1():    print('这里是func1')    def func2():        print('这里是func2')    return func2  # 把func2当成返回值返回ret = func1()  # 调用func1,把返回值赋值给retret()  # 调用ret

 

闭包

什么是闭包?

首先我们来看一个例子:

def func1():    name = '张三'    def func2(arg):        print(arg)    func2(name)func1()

理解了上面的例子,我们再看一个例子:

def func1():    name = '张三'    def func2():        print(name)  # 能够访问到外层作用域的变量    func2()func1()

一个内层函数中,引用了外层函数(非全局)的变量,这个内层函数就可以成为闭包。

在Python中,我们可以使用__closure__来检测函数是否是闭包。

def func1():    name = '张三'    def func2():        print(name)  # 能够访问到外层作用域的变量    func2()    print(func2.__closure__)  # (<cell at 0x1036c7438: str object at 0x10389d088>,)func1()print(func1.__closure__)  # None

问题来了,我们如何在函数外边调用函数内部的函数呢?

当然是把内部函数当成返回值返回了。

def func1():    name = '张三'    def func2():        print(name)    return func2  # 把内部函数当成是返回值返回ret = func1()  # 把返回值赋值给变量retret()  # 调用内部函数

内部函数当然还可包含其他的函数,多层嵌套的道理都是一样的。

def func1():    def func2():        def func3():            print('func3')        return func3    return func2ret1 = func1()  # func2ret2 = ret1()  # func3ret2()

从这个问题中我们可以引出闭包的好处。由于我们在外界可以访问到内部函数。那这个时候内部函数访问的时间和时机就不⼀定了, 因为在外部我们可以选择在任意时间去访问内部函数。但是我们知道如果⼀个函数执⾏完毕,则这个函数中的变量以及局部命名空间中的内容都将会被销毁。在闭包中如果变量被销毁了,那内部函数就不能正常执⾏。所以Python中如果一个内部函数引用了外层函数中的变量,那么这个变量将不会随着外层函数的结束而销毁,它会在内存中保留。

也就是说,使⽤闭包可以保留变量的引用。

装饰器

装饰器来历

在说装饰器之前,我们先说⼀个软件设计的原则: 开闭原则, ⼜被成为开放封闭原则。

开放封闭原则是指对扩展代码的功能是开放的,但是对修改源代码是封闭的。这样的软件设计思路可以保证我们更好的开发和维护我们的代码。

我们先来写一个例子,模拟一下女娲造人:

def create_people():    print('女娲真厉害,捏个泥吹口气就成了人!')    create_people()

好吧,现在问题来了。上古时期啊,天气很不稳定,这个时候突然大旱三年。女娲再去捏人啊,因为太干了就捏不到一块儿去了,需要在捏人之前洒点水才行。

def create_people():    print('洒点水')    print('女娲真厉害,捏个泥吹口气就成了人!')create_people()

这不就搞定了么?但是呢,我们是不是违背了开放封闭原则呢?我们是添加了新的功能,但是我们是直接修改了源代码。在软件开发中我们应该对直接修改源代码是谨慎的。

比如,女娲为了防止浪费,想用剩下点泥巴捏个鸡、鸭、鹅什么的,也需要洒点水。那我们能在每个造鸡、造鸭、造鹅函数的源代码中都手动添加代码么?肯定是不现实的。

怎么办?再写一个函数不就OK了么?

def create_people():    print('女娲真厉害,捏个泥吹口气就成了人!')def create_people_with_water():    print('洒点水')    create_people()create_people_with_water()

不让我直接修改源代码,那我重新写一个函数不就可以了吗?

但是,你有没有想过一个问题,女娲造人也很累的,她后来开了很多分店,每家分店都是调用了之前的create_people函数造人,那么你修改了之后,是不是所有调用原来函数的人都需要修改调用函数的名称呢?很麻烦啊!!!

总结一句话就是如何在不改变函数的结构和调用方式的基础上,动态的给函数添加功能?

def create_people():    print('女娲真厉害,捏个泥吹口气就成了人!')def a(func):    def b():        print('洒点水')        func()    return bret = a(create_people)ret()

利用闭包函数不就可以了么?

但是,你这最后调用的是ret啊,不还是改变了调用方式么?

再往下看:

def create_people():    print('女娲真厉害,捏个泥吹口气就成了人!')def a(func):    def b():        print('洒点水')        func()    return bcreate_people = a(create_people)create_people()

上面这段代码是不是完美解决了我们的问题呢?

看一下它的执行过程吧:

  1. 首先访问a(create_people)
  2. 把create_people函数赋值给了a函数的形参func,记住后续执行func的话实际上是执行了最开始传入的create_people函数。
  3. a函数执行过程就是一句话,返回了b函数。这个时候把b函数赋值给了create_people这个变量
  4. 执行create_people的时候,相当于执行了b函数,先打印洒点水再执行func,也就是我们最开始传入的create_people函数

我们巧妙的使用闭包实现了,把一个函数包装了一下,然后再赋值给原来的函数名。

装饰器语法糖

上面的代码就是一个装饰器的雏形,Python中针对于上面的功能提供了一个快捷的写法,俗称装饰器语法糖。

使用装饰器语法糖的写法,实现同样功能的代码如下:

def a(func):    def b():        print('洒点水')        func()    return b@a  # 装饰器语法糖def create_people():    print('女娲真厉害,捏个泥吹口气就成了人!')create_people()

 

装饰器进阶

来源:http://www.icode9.com/content-1-75451.html
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
12步轻松搞定python装饰器
《源码探秘 CPython》64. 装饰器是怎么实现的?
【连载电子书五】Python函数编程(下)
Python 五个知识点搞定作用域
函数基础语法及使用
Python装饰器学习(九步入门)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服