闭包(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, 也就是说它们共享着同一个变量ii是会改变的,当函数调用时,循环变量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 中的闭包与装饰器的更多相关文章

  1. python中的闭包和装饰器

    重新学习完了函数,是时候将其中的一些重点重新捋一捋了,本次总结的东西只有闭包和装饰器 1.闭包 闭包是python函数中的一个比较重要功能,一般闭包都是用在装饰器上,一般学完闭包就会去学习装饰器,这俩 ...

  2. 21.python中的闭包和装饰器

    python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure). 以下说明主要针对 python ...

  3. 轻松理解python中的闭包和装饰器 (下)

    在 上篇 我们讲了python将函数做为返回值和闭包的概念,下面我们继续讲解函数做参数和装饰器,这个功能相当方便实用,可以极大地简化代码,就让我们go on吧! 能接受函数做参数的函数我们称之为高阶函 ...

  4. 轻松理解python中的闭包和装饰器(上)

    继面向对象编程之后函数式编程逐渐火起来了,在python中也同样支持函数式编程,我们平时使用的map, reduce, filter等都是函数式编程的例子.在函数式编程中,函数也作为一个变量存在,对应 ...

  5. 聊聊Python中的闭包和装饰器

    1. 闭包 首先我们明确一下函数的引用,如下所示: def test1(): print("--- in test1 func----") # 调用函数 test1() # 引用函 ...

  6. python中的闭包与装饰器

    #原创,转载请留言联系 装饰器的本质就是闭包,所以想知道装饰器是什么,首先要理解一下什么是闭包. 闭包 1. 外部函数返回内部函数的引用.2. 内部函数使用外部函数的变量或者参数. def outer ...

  7. python中函数总结之装饰器闭包

    1.前言 函数也是一个对象,从而可以增加属性,使用句点来表示属性. 如果内部函数的定义包含了在外部函数中定义的对象的引用(外部对象可以是在外部函数之外),那么内部函数被称之为闭包. 2.装饰器 装饰器 ...

  8. python基础16_闭包_装饰器

    不了解是否其他语言也有类似 python 装饰器这样的东西. 最近才发现ECMAScript6也是有生成器函数的,也有 yield  generator 装饰器的基础知识是闭包: # 闭包:嵌套函数, ...

  9. 第十七篇 Python函数之闭包与装饰器

    一. 装饰器 装饰器:可以拆解来看,器本质就是函数,装饰就是修饰的意思,所以装饰器的功能就是为其他函数添加附加功能. 装饰器的两个原则: 1. 不修改被修饰函数的源代码 2. 不修改被修饰函数的调用方 ...

随机推荐

  1. 【HLSDK系列】服务端实体 edict_t 和 控制类

    我们来了解一下引擎是怎么管理实体的吧!我们这里就说说服务端的实体(edict_t) 服务端用 edict_t 这个结构体来保存一个实体,可以说一个 edict_t 就是一个 服务端实体,下文简称实体. ...

  2. 关于upper、lower bound 的探讨

    lower_bound(A, A+n, x) - A  返回第一个大于等于x的数的下标 lower_bound(A, A+n, x) - A - 1 返回最后一个小于x的数的下标 upper_boun ...

  3. Django路由分发

    1. 先自己手动在项目APP01以及APP02下面各有一个urls.py.当请求来了的时候,先到工程中的urls找路由分发,然后再转到各个App中. 现在总共有3个url 2. 工程中的urls如下: ...

  4. hive 分区表和分桶表

    1.创建分区表 hive> create table weather_list(year int,data int) partitioned by (createtime string,area ...

  5. 漫谈ElasticSearch关于ES性能调优几件必须知道的事

    lasticSearch是现在技术前沿的大数据引擎,常见的组合有ES+Logstash+Kibana作为一套成熟的日志系统,其中Logstash是ETL工具,Kibana是数据分析展示平台.ES让人惊 ...

  6. 如何使用火狐下的两款接口测试工具RESTClient和HttpRequester发送post请求

    Chrome下有著名的Postman,那火狐也有它的左膀右臂,那就是RESTClient和HttpRequester.这两款工具都是火狐的插件,主要用来模拟发送HTTP请求,HTTP请求最常用的两种方 ...

  7. Android 解决ScrollView嵌入ListView | GridView | ScrollView显示问题

    一.ScrollView中嵌套ListView ScrollView和ListView都是滚动结构,很明显如果在ScrollView中加入ListView,可以预见性的知道,肯定会有显示/滚动的问题, ...

  8. 【BZOJ3166】ALO(主席树)

    [BZOJ3166]ALO(主席树) 题面 权限题qwq 资磁洛谷 题解 用一个\(set\)求出左右侧比这个数大的第\(2\)个数, 然后用可持久化\(Trie\)算一下就好啦 #include&l ...

  9. BZOJ3527:[ZJOI2014]力——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=3527 给出n个数qi,给出Fj的定义如下: 令Ei=Fi/qi,求Ei. 参考:https://ww ...

  10. POJ1201 Intervals 【差分约束】

    题目链接 POJ1201 题解 差分约束 令\(a[i]\)表示是否选择\(i\),\(s[i]\)表示\(a[i]\)的前缀和 对\(s[i] \quad i \in [-1,50000]\)分别建 ...