目录

  • 目录
  • 闭包
    • 函数的实质和属性
    • 闭包有什么好处
    • 小结
  • 装饰器
    • 更加深入的看看装饰器的执行过程
    • 带参数的装饰器
    • 装饰器的叠加
    • 小结
    • 装饰器能解决什么问题
      • 小结

闭包

Closure: 如果内层函数引用了外层函数的局部变量(L),并且在外层函数中 return 内层函数时,这种关系就称之为闭包。
闭包的特点就是返回的内层函数还引用了外层函数的局部变量,所以要想正确的使用闭包,那么就要确保这个被内层函数引用的局部变量是不变的。
EXAMPLE:

In [71]: def count():...:     fs = []...:     for i in range(1, 4):...:         def f():...:              return i*i...:         fs.append(f)...:     return fs...:...: f1, f2, f3 = count()...:In [72]: f1,f2,f3
Out[72]: (<function __main__.f>, <function __main__.f>, <function __main__.f>)In [73]: f1(),f2(),f3()
Out[73]: (9, 9, 9)

最总 f1(),f2(),f3() 输出的是 (9,9,9) 而不是 (1,4,9)
原因就是当 count() 函数返回了 f1(),f2(),f3() 3个函数时,这3个函数所引用的变量 i 的值已经变成了 3。由于 f1、f2、f3 并没有被调用,所以,此时他们并未计算 i*i 。返回函数不要引用任何循环变量,或者后续会发生变化的变量。

函数的实质和属性

  • 函数是一个对象,在内存中会占用存储空间
  • 一般来说函数执行完成后其内部的变量都会被回收。但如果函数中的变量被 return 时,那么这个变量是不会被回收的,因为其引用计数 != 0
  • 函数具有属性
  • 函数具有返回值
  • 函数通过函数名来引用

EXAMPLE:

passline = 60
def func(val):print 'id(val): %x' % id(val)if val >= passline:print "pass"else:print "failed"def in_func():print valin_func()return in_funcf = func(89)
f()print (f.__closure__)

Output:

In [33]: run demo_1.py
id(val): 33c6548     # 变量 val 在内存中的地址
pass
89
(<cell at 0x0000000004E22588: int object at 0x00000000033C6548>,)  # 函数 f 包含了一个 int 对象,这个 int 对象的引用跟变量 val 是相同的

NOTE1: in_func() 引用了嵌套作用域(E)中的变量 val
NOTE2: 并且内层函数 in_func() 被外层函数 func() return。 所以,val 这个变量被包含在了函数 in_func() 中一起被返回给了变量 f,即:变量 val 被变量 f 所引用,所以这个变量 val 并没有在函数 func() 执行结束之后被回收。

总结: 外层函数局部作用域中的变量能够被内部函数所引用。

闭包有什么好处?

因为闭包是通过 return 一个函数来定义的,所以我们可以把一些函数延迟执行。EXAMPLE:

def calc_sum(lst):def lazy_sum():return sum(lst)return lazy_sum

调用calc_sum()并没有计算出结果,而是返回函数:

>>> func = calc_sum([1, 2, 3, 4])
>>> func
<function lazy_sum at 0x1037bfaa0>

对返回的函数进行调用时,才计算出结果:

>>> func()
10

由于可以返回函数,所以我们在后续代码里就可以灵活的决定到底要不要调用该返回的函数。

再看下面一个例子:

def set_passline(passline):def cmp(val):if val >= passline:print "pass"else:print "failed"return cmpf_100 = set_passline(60)
f_100(89)
print "f_100 type: %s" % type(f_100)
print "f_100 param: %s" % f_100.__closure__
print '*' * 20
f_150 = set_passline(90)
f_150(89)

Output:

In [37]: run demo_1.py
pass
f_100 type: <type 'function'>
f_100 param: <cell at 0x0000000004D5A468: int object at 0x00000000033C6038>
********************
failed

set_passline() 和 cmp() 组成一个闭包,最明显的好处在于可以提高 cmp() 函数内代码实现的复用性和封装(内层函数不能在全局作用域中被直接调用)。不需要为两个不用的判断(100 | 150)而写两个相似的函数。

而且外层函数中的局部变量还可以是一个函数。
继续看下一个例子:

def my_sum(*arg):print "my_sum"print argreturn sum(arg)def my_average(*arg):return sum(arg)/len(arg)def dec(func):print "dec"print funcdef in_dec(*arg): print "in_dec"print funcprint argif len(arg) == 0:return 0for val in arg:if not isinstance(val, int):return 0return func(*arg)return in_dec# dec(my_sum)(1, 3, 4)
my_sum_result = dec(my_sum)
print my_sum_result(1, 3, 4)
print my_sum_result.__closure__print '*' * 40my_average_result = dec(my_average)
print my_average_result(1, 3, 4)
print my_average_result.__closure__

Output:

In [35]: run demo_1.py
dec
<function my_sum at 0x00000000048F1208>
in_dec
<function my_sum at 0x00000000048F1208>
(1, 3, 4)
my_sum
(1, 3, 4)
8
(<cell at 0x0000000004A7C618: function object at 0x00000000048F1208>,)
****************************************
dec
<function my_average at 0x00000000048F1A58>
in_dec
<function my_average at 0x00000000048F1A58>
(1, 3, 4)
2
(<cell at 0x0000000004A7CE58: function object at 0x00000000048F1A58>,)

小结

  • 闭包能够提高代码的复用和内层函数的封装
  • 内层函数引用外层函数的局部变量可以是基本数据类型对象,也可以是一个函数对象
  • 调用外层函数(传递外层形参)时,返回的是一个内存函数对象,然后可以再对内层函数传递形参,这样就增强了代码的灵活性。EG. 由外层设定条件,内层进行处理。

当然我们可以能回认为这样的写法并不美观,所以我们继续看 Python 的装饰器和闭包的关系。

装饰器

装饰器实际上就是一个函数, 而且是一个接收函数对象的函数. 有了装饰器我们可以在执行被装饰函数之前做一个预处理, 也可以在处理完函数之后做清除工作. 所以在装饰器函数中你会经常看见这样的代码: 函数体定义了一个内嵌函数, 并在内嵌函数内实现了目标函数(被装饰函数)的调用.这是一种 AOP 面向方法编程的方式.
- 装饰器用于装饰函数 ⇒ dec() 用于装饰 my_sum()
- 装饰器返回一个新的函数对象 ⇒ dec() 返回一个函数 in_dec()
- 被装饰的函数标识符指向返回的函数对象 ⇒ my_sum() 指向 in_dec() my_sum_result = dec(my_sum) 即: my_sum_resultdec() 返回的函数对象,引用指向了 my_sum()
- 语法糖:@装饰器名称

从上面几点特性可以看出,装饰器就是对闭包的使用。
将上面的例子转换一下写法:

def dec(func):print 'call dec()'def in_dec(*arg): print "call in_dec()"if len(arg) == 0:return 0for val in arg:if not isinstance(val, int):return 0return func(*arg)return in_dec# @dec 执行的过程:
# 1. dec(my_sum) --> return in_dec
# 2. my_sum = in_dec
# 为了丰富被装饰函数的功能(这里是丰富了一个参数验证功能),是一种抽象的编程思维
@dec
def my_sum(*arg):print 'call my_sum()'return sum(arg)def my_average(*arg):return sum(arg)/len(arg)print my_sum(1,2,3,4,5)

Output:

In [43]: run demo_1.py
call dec()
call in_dec()
call my_sum()
15

更加深入的看看装饰器的执行过程

  • chocolate() 只是一个普通的函数
def chocolate():print "call chocolate()"
chocolate()

Output:

In [65]: run demo_1.py
call chocolate()

正常的调用了 chocolate() 函数

  • chocolate() 加上了一个没有返回内存函数的装饰器
def jmilkfan(func):def in_jmilkfan():print "call in_jmilkfan()"func()print "call jmilkfan()"@jmilkfan
def chocolate():print "call chocolate()"
chocolate()

Output:

In [51]: run demo_1.py
call jmilkfan()  # 首先调用了 jmilkfan 装饰器对应的 jmilkfan() 函数---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
D:\Development\Python27\workspace\demo_1.py in <module>()9     print "call chocolate()"10 #print 'typr chocolate:%s' % type(chocolate)
---> 11 chocolate()TypeError: 'NoneType' object is not callable

**ERROR:**chocolate 是一个’NoneType’对象
因为 jmilkfan() 默认 return 了一个 None, 而不是内层函数 in_jmilkfan(),这里说明装饰器对应的 jmilkfan() 的确被调用了。但却没有返回内层函数给 chocolate 接收。

def jmilkfan(func):print 'func id:%x' % id(func)def in_jmilkfan():print "call in_jmilkfan()"func()print "call jmilkfan()"print 'in_jmilkfan():%s' % in_jmilkfan.__closure__return in_jmilkfan@jmilkfan
def chocolate():print 'chocolate():%s' % chocolate.__closure__print "call chocolate()"
chocolate()

Output:

In [67]: run demo_1.py
func id:490ecf8
call jmilkfan()
in_jmilkfan():<cell at 0x0000000004B0F5B8: function object at 0x000000000490ECF8>
call in_jmilkfan()
chocolate():<cell at 0x0000000004B0F5B8: function object at 0x000000000490ECF8>
call chocolate()

可看出 choholate()、 in_jmilkfan()、 func 都是引用了同一个函数对象。即: in_jmilkfan() 被 chocolate() 接收了。

注意当被装饰的函数拥有形参时,装饰器的内层函数必须定义相同的形参,否则会报错。

def jmilkfan(func):print 'func id:%x' % id(func)def in_jmilkfan(x, y):print "call in_jmilkfan()"func(x, y)print "call jmilkfan()"print 'in_jmilkfan():%s' % in_jmilkfan.__closure__return in_jmilkfan@jmilkfan
def chocolate(x, y):print 'chocolate():%s' % chocolate.__closure__print "call chocolate(), the value is:%d" % (x + y)chocolate(1, 2)

Output:

In [70]: run demo_1.py
func id:490edd8
call jmilkfan()
in_jmilkfan():<cell at 0x0000000004AF3288: function object at 0x000000000490EDD8>
call in_jmilkfan()
chocolate():<cell at 0x0000000004AF3288: function object at 0x000000000490EDD8>
call chocolate(), the value is:3

带参数的装饰器

@decomaker(deco_args)
def foo():pass# 等效于foo = decomaker(deco_args)(foo)

带参数的装饰器和不带参数的装饰器的区别在于, 前者需要先传入一个参数并返回一个函数对象, 该函数对象才是实际作用于被装饰函数的装饰器.

装饰器的叠加

@deco1(deco_args)
@deco2
def func():pass#等效于func = deco1(deco_args)(deco2(func))

小结

装饰器的作用: 在提供了闭包的工作之外,优雅了语法

装饰器的使用过程

  • 调用了装饰器对应的外层函数,并且将被装饰函数作为实参传递给外层函数。目的是为了能够让内存函数使用这个作为外层函数的局部变量的被装饰函数,从而执行被装饰函数的代码实现。
  • 外层函数将内层函数的引用返回给被装饰函数,实现闭包。

装饰器能解决什么问题?

问题:如果我们定义了一个函数后,又想在运行时动态的为这个函数增加功能,但是又不希望改动这个函数本身的代码?那么我们能否将这个函数作为一个参数传递给另一个函数,从而产生一个拥有更多功能的新函数呢 ?

这就用到了所谓的高阶函数:
1. 可以接受函数作为参数(函数又函数名来引用,函数名的本质就是一个变量)
2. 可以返回一个函数
这样高阶函数就能够到达我们希望接收一个函数并为其进行包装后再返回一个新的函数的效果。
EXAMPLE:

func = new_func(func)
# new_func 是一个高阶函数,也可以成为一个装饰器
# 因为函数是通过函数名来引用的,所以我们可以将一个由高阶函数返回的新的函数再次赋值给原来的函数名
# 这样的话 func 的原始定义函数就被彻底隐藏起来了,达到了封装的效果
# Python 还可以通过 @ 来简化装饰器的调用, EG:
@new_func
def func(x):...

小结

装饰器可以极大的简化代码,避免每个函数编写重复性的代码。
我们可以将需要被重复且希望动态调用的代码写成装饰器,EG:
引入日志(打印日志):@log
检测性能(增加计时逻辑来监测性能):@performance
加入事务能力(数据库事务):@transaction
URL 路由:@post('/register')

Python 进阶_闭包 amp; 装饰器相关推荐

  1. Python 进阶_闭包 装饰器

    目录 目录 闭包 函数的实质和属性 闭包有什么好处 小结 装饰器 更加深入的看看装饰器的执行过程 带参数的装饰器 装饰器的叠加 小结 装饰器能解决什么问题 小结 闭包 Closure: 如果内层函数引 ...

  2. Python中的闭包与装饰器及工厂模式

    每日一怼:没想到你年纪轻轻就会用脸吓人了 笔者对闭包,装饰器及工厂模式的理解为使用函数的内置嵌调用. 我们先从比闭包开始去认知装饰器和工厂模式. 闭包:外部函数定义的内部函数就是闭包. 闭包的作用及好 ...

  3. python闭包与装饰器有啥关系_深入理解Python中的闭包与装饰器

    函数的装饰器可以以某种方式增强函数的功能,如在 Flask 中可使用 @app.route('/') 为视图函数添加路由,是一种十分强大的功能.在表现形式上,函数装饰器为一种嵌套函数,这其中会涉及到闭 ...

  4. python闭包和装饰器的区别_深入理解Python中的闭包与装饰器

    函数的装饰器可以以某种方式增强函数的功能,如在 Flask 中可使用 @app.route('/') 为视图函数添加路由,是一种十分强大的功能.在表现形式上,函数装饰器为一种嵌套函数,这其中会涉及到闭 ...

  5. python中的闭包与装饰器教程

    一.闭包 闭包相当于函数中,嵌套另一个函数,并返回. 代码如下: def func(name): # 定义外层函数def inner_func(age): # 内层函数print('name: ', ...

  6. python进阶:搞懂装饰器和切面编程

    学习目标: 装饰器.切面编程,实际使用体会. 纸上学来终觉浅 装饰器和切面编程: 常见的函数,我们一般的返回值一个常见的数值或者列表,比如: def mysum(a,b):return a + b r ...

  7. python反射、闭包、装饰器_python之闭包、装饰器、生成器、反射

    1. 函数的作用域 1.在函数定义时候就固定,与调用位置无关,在调用的时候返回到函数定义的位置 x=1 #全局作用 def f1(): #整个f1函数是定义阶段 deff2():print(x)ret ...

  8. python反射、闭包、装饰器_python 闭包装饰器(一)

    一.闭包 1.举例 defouter(): x= 10 def inner(): #内部函数 print(x) #外部函数的一个变量 returninner#调用inner()函数的方法 outer( ...

  9. 1Python进阶强化训练之装饰器使用技巧进阶

    Python进阶强化训练之装饰器使用技巧进阶 如何使用函数装饰器? 实际案例 某些时候我们想为多个函数,统一添加某种功能,比如记时统计.记录日志.缓存运算结果等等. 我们不想在每个函数内一一添加完全相 ...

最新文章

  1. 命名实体识别NER遗留问题----模型构建
  2. ssh项目实战----Spring计时器任务 Spring整合JavaMail(邮件发送)
  3. php loading效果,利用CSS3打造十种Loading效果
  4. linux内存使用监控,如何利用Bash脚本监控Linux的内存使用情况
  5. Linux C++多线程同步的四种方式
  6. 提前祝大家十一中秋节快乐
  7. 单点登录 之 OAuth
  8. [Android Studio] Android Studio如何提示函数用法
  9. 10年+,阿里沉淀出怎样的搜索引擎?
  10. JUC包中的分而治之策略-为提高性能而生 1
  11. 数学建模学习笔记(七)——图论最短路问题
  12. Windows下安装MySQL5.7流程
  13. Linux 内存泄漏检查工具 valgrind
  14. (12)Spring框架----Spring框架是如何配置数据库资源的
  15. TiDB DevCon2018.tick(1.20)
  16. linux的防火墙端口配置
  17. 沃尔玛经典营销案例:啤酒与尿布
  18. UG基础知识学习视频目录整理(制图篇)
  19. 画虎画皮难画骨,编程编码难编译
  20. COSELF 次元秀场伦敦时装周预告 #虚拟时尚

热门文章

  1. ddos攻击python_Python基于http的ddos攻击代码
  2. python十进制转八进制_怎样用python进行二进制,八进制,十进制转换
  3. 网站如何进行渠道跟踪_开发网站不知道如何进行推广?5个技巧教会你
  4. java 什么时候进行垃圾回收_Java中垃圾回收有什么目的?什么时候进行垃圾回收?...
  5. django html5 video,Django Web中的静态文件之HTML5第1篇
  6. 全局变量中断原子操作_原子操作原理分析
  7. 如何从MP4视频文件中抽取MP3音频?
  8. 利用 scipy.signal中的spectrogram分析信号的时频联合分布
  9. 测试简易混沌振荡电路一些因素的影响
  10. 第十六届全国大学生智能车竞赛创意组-讯飞智慧餐厅