Python 中的闭包与装饰器
闭包(closure)是函数式编程的重要的语法结构。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。
如果在一个内嵌函数里,对在外部函数内(但不是在全局作用域)的变量进行引用,那么内嵌函数就被认为是闭包(closure)。
定义在外部函数内但由内部函数引用或者使用的变量称为自由变量。
总结一下,创建一个闭包必须满足以下几点:
- 1. 必须有一个内嵌函数
- 2. 内嵌函数必须引用外部函数中的变量
- 3. 外部函数的返回值必须是内嵌函数
1.闭包使用示例
先看一个闭包的例子:
In [10]: def func(name):
...: def in_func(age):
...: print 'name:',name,'age:',age
...: return in_func
...:
In [11]: demo = func('feiyu')
In [12]: demo(19)
name: feiyu age: 19
这里当调用 func
的时候就产生了一个闭包——in_func
,并且该闭包持有自由变量——name
,因此这也意味着,当函数func
的生命周期结束之后,name
这个变量依然存在,因为它被闭包引用了,所以不会被回收。
在 python
的函数内,可以直接引用外部变量,但不能改写外部变量,因此如果在闭包中直接改写父函数的变量,就会发生错误。看以下示例:
实现一个计数闭包的例子:
def counter(start=0):
count = [start]
def incr():
count[0] += 1
return count
return incr
a = counter()
print 'a:',a
In [32]: def counter(start=0):
...: count = start
...: def incr():
...: count += 1
...: return count
...: return incr
...:
In [33]: a = counter()
In [35]: a() #此处会报错
UnboundLocalError: local variable 'count' referenced before assignment
应该像下面这样使用:
In [36]: def counter(start=0):
...: count = [start]
...: def incr():
...: count[0] += 1
...: return count
...: return incr
...:
In [37]: count = counter(5)
In [38]: for i in range(10):
...: print count(),
...:
[6] [7] [8] [9] [10] [11] [12] [13] [14] [15]
2.使用闭包的陷阱
In [1]: def create():
...: return [lambda x:i*x for i in range(5)] #推导式生成一个匿名函数的列表
...:
In [2]: create()
Out[2]:
[<function __main__.<lambda>>,
<function __main__.<lambda>>,
<function __main__.<lambda>>,
<function __main__.<lambda>>,
<function __main__.<lambda>>]
In [4]: for mul in create():
...: print mul(2)
...:
8
8
8
8
8
结果是不是很奇怪,这算是闭包使用中的一个陷阱吧!来看看为什么?
在上面的代码当中,函数create
返回一个list
里面保存了4个函数变量,这4个函数都共同的引用了循环变量i
, 也就是说它们共享着同一个变量i
,i
是会改变的,当函数调用时,循环变量i
已经是等于4了,因此4个函数返回的都是8。如果,需要在闭包使用循环变量的值的话,把循环变量作为闭包的默认参数或者是通过偏函数来实现。实现的原理也很简单,就是当把循环变量当参数传入函数时,会申请新的内存。示例代码如下:
In [5]: def create():
...: return [lambda x,i=i:i*x for i in range(5)]
...:
In [7]: for mul in create():
...: print mul(2)
...:
0
2
4
6
8
3,闭包与装饰器
装饰器就是一种的闭包的应用,只不过其传递的是函数:
def addb(func):
def wrapper():
return '<b>' + func() + '</b>'
return wrapper
def addli(func):
def wrapper():
return '<li>' + func() + '</li>'
return wrapper
@addb # 等同于 demo = addb(addli(demo))
@addli # 等同于 demo = addli(demo)
def demo():
return 'hello world'
print demo() # 执行的是 addb(addku(demo))
在执行时,首先将demo
函数传递给addli
进行装饰,然后将装饰后的函数传递给addb
进行装饰。所以最后返回的结果是:
<b><li>hello world</li></b>
4.装饰器中的陷阱
当你写了一个装饰器作用在某个函数上,这个函数的重要的元信息比如名字、文档字符串、注解和参数签名都会丢失。
def out_func(func):
def wrapper():
func()
return wrapper
@out_func
def demo():
"""
this is a demo.
"""
print 'hello world.'
if __name__ == '__main__':
demo()
print "__name__:",demo.__name__
print "__doc__:",demo.__doc__
看结果:
hello world.
__name__: wrapper
__doc__: None
函数名字和文档字符串都变成了闭包的信息。好在可以使用 functools
库中的 @wraps
装饰器来注解底层包装函数。
from functools import wraps
def out_func(func):
@wraps(func)
def wrapper():
func()
return wrapper
自己试试结果吧!
学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入学习交流群
626062078,我们一起学Python!
Python 中的闭包与装饰器的更多相关文章
- python中的闭包和装饰器
重新学习完了函数,是时候将其中的一些重点重新捋一捋了,本次总结的东西只有闭包和装饰器 1.闭包 闭包是python函数中的一个比较重要功能,一般闭包都是用在装饰器上,一般学完闭包就会去学习装饰器,这俩 ...
- 21.python中的闭包和装饰器
python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure). 以下说明主要针对 python ...
- 轻松理解python中的闭包和装饰器 (下)
在 上篇 我们讲了python将函数做为返回值和闭包的概念,下面我们继续讲解函数做参数和装饰器,这个功能相当方便实用,可以极大地简化代码,就让我们go on吧! 能接受函数做参数的函数我们称之为高阶函 ...
- 轻松理解python中的闭包和装饰器(上)
继面向对象编程之后函数式编程逐渐火起来了,在python中也同样支持函数式编程,我们平时使用的map, reduce, filter等都是函数式编程的例子.在函数式编程中,函数也作为一个变量存在,对应 ...
- 聊聊Python中的闭包和装饰器
1. 闭包 首先我们明确一下函数的引用,如下所示: def test1(): print("--- in test1 func----") # 调用函数 test1() # 引用函 ...
- python中的闭包与装饰器
#原创,转载请留言联系 装饰器的本质就是闭包,所以想知道装饰器是什么,首先要理解一下什么是闭包. 闭包 1. 外部函数返回内部函数的引用.2. 内部函数使用外部函数的变量或者参数. def outer ...
- python中函数总结之装饰器闭包
1.前言 函数也是一个对象,从而可以增加属性,使用句点来表示属性. 如果内部函数的定义包含了在外部函数中定义的对象的引用(外部对象可以是在外部函数之外),那么内部函数被称之为闭包. 2.装饰器 装饰器 ...
- python基础16_闭包_装饰器
不了解是否其他语言也有类似 python 装饰器这样的东西. 最近才发现ECMAScript6也是有生成器函数的,也有 yield generator 装饰器的基础知识是闭包: # 闭包:嵌套函数, ...
- 第十七篇 Python函数之闭包与装饰器
一. 装饰器 装饰器:可以拆解来看,器本质就是函数,装饰就是修饰的意思,所以装饰器的功能就是为其他函数添加附加功能. 装饰器的两个原则: 1. 不修改被修饰函数的源代码 2. 不修改被修饰函数的调用方 ...
随机推荐
- DRM Study
1.DRM是什么? DRM,英文全称Digital Rights Management, 可以翻译为:数字版权管理.指的是出版者用来控制被保护对象的使用权的一些技术,这些技术保护的有数字化内容(例如: ...
- 洛谷 P1865 A % B Problem
题目戳 题目背景 题目名称是吸引你点进来的 实际上该题还是很水的 题目描述 区间质数个数 输入输出格式 输入格式: 一行两个整数 询问次数n,范围m 接下来n行,每行两个整数 l,r 表示区间 输出格 ...
- Simpsons’ Hidden Talents HDU - 2594(拓展kmp)
Sample Input clinton homer riemann marjorie Sample Output 0 rie 3 看输出才题意...拓展kmp特征很明显嘛....注意开始就匹配到尾的 ...
- Eve-NG-Toolkit
Eve-NG-Toolkit 来源 http://www.emulatedlab.com/archives/694 参考 http://eve-ng.cn/doku.php http://foru ...
- poll() 与 select()比较
比较poll() 与select() 尽管poll()和select()所做的是相同的工作,不过poll()优于select(),原因: 1.poll()不需要用户计算并传递作为参数的最高编号的 ...
- 【JQuery】文档操作
一.前言 接着上一章的内容,接着JQuery的学习 二.内容 addClass 向被选元素添加一个或多个类 $(selector).addClass(class) $(selector).addCla ...
- smb(ms17-010)远程命令执行之msf
本次用到的环境: kali(2016.2)32位系统.ip地址:192.168.1.104 目标靶机为:win7sp1x64系统(关闭防火墙),ip地址:192.168.1.105 具体的步骤如下: ...
- 流媒体协议之RTSP客户端的实现20171014
RtspClient是基于jrtplib实现的,目前仅支持h264格式,后续将不断迭代优化,加入对其他格式的支持,并且将实现RTSP的服务端. RtspClient的功能是接收服务端过来流,然后写入到 ...
- 流媒体协议之JRTPLIB的使用20170919
主要介绍JRTPLIB 2.x系列和3.x系列两种版本,它们的区别是2.x系列代码量少使用简单,但是只支持RFC 1889不支持RFC 3550,3.x支持RFC 3550,但代码量稍多,以及使用也稍 ...
- webstorm下搭建编译less环境 以及设置压缩css
webstorm自带less,不过要编译的话需要nodejs环境. 首先去node的主页下载对应版本的nodejs然后安装,下载地址:http://nodejs.org/ 安装完之后打开命令提示符(w ...