函数名是⼀个变量,但它是⼀个特殊的变量。与括号配合可以执⾏函数的变量。
查看函数名的内存地址:
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()
上面这段代码是不是完美解决了我们的问题呢?
看一下它的执行过程吧:
我们巧妙的使用闭包实现了,把一个函数包装了一下,然后再赋值给原来的函数名。
上面的代码就是一个装饰器的雏形,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联系客服