闭包广泛使用在函数式编程语言中,虽然不是很容易理解,但是又不得不理解。

闭包是什么?

在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。

—— 维基百科)

举个例子

def sum(a,b):
return a+b def sum1(a):
def add(b):
return a+b #a为外部变量
return add #返回函数 type(sum(1,2)) #<class 'int'>
type(sum1(1)) #<class 'function'>

一般支持将函数当做对象使用的编程语言,如Python,JavaScript都支持闭包

如何理解闭包

闭包存在的意义是夹带了外部变量,如果没有的话,其实和普通函数没有区别。同一个函数夹带了不同的私货就是不同的闭包,可以理解为对函数的轻量级的封装。

下面这个例子是计算数字的平方和立方,如果是用普通函数,要么是需要写两个函数,要么需要传两个参数

def rlt(v):
def product(num):
return num ** v
return product square = rlt(2)
cube = rlt(3) print(square(2), cube(2)) # 4, 8

闭包传递了某些变量,对使用者来说就便捷了很多,下面会讲到闭包的原理

总结下:

闭包其实和普通函数的区别:

1、普通函数传递变量,闭包传递函数

2、闭包的封装性更好,调用的参数更少

什么时候用闭包?

1. 装饰器

闭包在python中非常常见,但是可能很难意识到闭包的存在。这里不得不提到Python中的装饰器Decorator。

装饰器顾名思义是装饰作用。在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

举个例子,

计算函数运行时间,正常写法

import  time

def do_sth():
time.sleep(3) startTime = time.time()
do_sth()
endTime = time.time()
print("do_sth run {} ".format(endTime-startTime)) #do_sth run 3.0005998611450195

如果我们要计算别的函数运行时间,就要重复多次代码,我们把这些重复代码放到装饰器里去,如下面代码

import  time

def timer(fun):
def wrapper():
startTime = time.time()
fun()
endTime = time.time()
print("{} run {}".format(func.__name__, endTime - startTime))
return wrapper @timer
def do_sth():
time.sleep(3) timer(do_sth)() # 一:不加@timer语法糖的调用方式
do_sth() #二:加@timer语法糖的调用方式, 和方式一等价

@timer放到do_sth的函数定义前,相当于执行了

do_sth = timer(do_sth)

装饰器Pythonic的调用方式完全和普通函数调用方式一样,是不是很方便?

如果装饰器需要带参数呢?那就需要再加一层,用于接收这些函数。又是一层的闭包。

import  time

def timer(text):
def decorator(fun):
def wrapper():
startTime = time.time()
fun()
endTime = time.time()
print("{} {} total run time:{}".format(text, fun.__name__, endTime - startTime))
return wrapper
return decorator @timer('excute')
def do_sth():
time.sleep(3)

三层嵌套的效果是

do_sth = timer('excute')(do_sth)

想一想下面的代码打印结果是什么?

print(do_sth.__name__)
print(timer('excute').__name__)
print(timer('excute')(do_sth).__name__)

do_sth.__name__的结果不再是do_sth。这里需要把原始函数的__name__等属性赋值到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。

wrapper.__name__ = func.__name__ ? 不用,Python内置的functools.wraps就是做这个的

另外,之前写的wrapper是不带参数的,只适配不带参数的函数调用,如果是doActive(active, f)则无法使用。所以更新定义:def wrapper(*args, **kw):

于是,一个完整的不带参数的decorator的写法如下:

import time
import functools def timer(fun):
@functools.wraps(fun)
def wrapper(*args, **kw):
startTime = time.time()
fun(*args, **kw)
endTime = time.time()
print("{} {} total run time:{}".format(fun.__name__, endTime - startTime))
return wrapper @timer
def do_sth():
time.sleep(3) print(do_sth.__name__) #do_sth

试试改写上面的带参数的decorator

import  time
import functools def timer(text):
@functools.wraps(timer)
def decorator(fun):
@functools.wraps(fun)
def wrapper():
startTime = time.time()
fun()
endTime = time.time()
print("{} {} total run time:{}".format(text, fun.__name__, endTime - startTime))
return wrapper
return decorator @timer('excute')
def do_sth():
time.sleep(3)

这次如下代码的运行结果是?

print(do_sth.__name__)
print(timer('excute').__name__)
print(timer('excute')(do_sth).__name__)

2. 惰性求值

常用于数据库访问的时候

# 伪代码示意

class QuerySet(object):
def __init__(self, sql):
self.sql = sql
self.db = Mysql.connect().corsor() # 伪代码 def __call__(self):
return db.execute(self.sql) def query(sql):
return QuerySet(sql) result = query("select name from user_app")
if time > now:
print result # 这时才执行数据库访问

上面这个不太恰当的例子展示了通过闭包完成惰性求值的功能,但是上面query返回的结果并不是函数,而是具有函数功能的类。有兴趣的可以去看看Django的queryset的实现,原理类似。

3.需要对某个函数的参数提前赋值

Python中已经有了很好的解决访问 functools.parial,但是用闭包也能实现。

def partial(**outer_kwargs):
def wrapper(func):
def inner(*args, **kwargs):
for k, v in outer_kwargs.items():
kwargs[k] = v
return func(*args, **kwargs)
return inner
return wrapper @partial(age=15)
def say(name=None, age=None):
print name, age say(name="the5fire")
# 当然用functools比这个简单多了
# 只需要: functools.partial(say, age=15)(name='the5fire')

python偏函数int2 = functools.partial(int, base=2),可以类比C++的bind1st, bind2nd

闭包的原理?

闭包其实也是一种函数,普通函数的__closure__None,闭包里是是一个元组,存放着所有的cell对象,每个cell`对象保存着这个闭包里所有的外部变量。

def sum(a, b):
return a+b
print(sum.__closure__) #None def rlt(v):
def product(num):
return num ** v
return product square = rlt(2)
cube = rlt(3) print(square.__closure__) #(<cell at 0x0000000001E2F768: int object at 0x000007FEF25E62B0>,)
for x in square.__closure__:
print(x.cell_contents) #2 print(cube.__closure__)
for x in cube.__closure__: #(<cell at 0x0000000001E2F798: int object at 0x000007FEF25E62D0>,)
print(x.cell_contents) #3

参考资料:

廖雪峰 Python装饰器

Python中的闭包

[Python]闭包的理解和使用的更多相关文章

  1. python闭包的理解说明

    什么是闭包: 闭包(closure)是函数式编程的重要的语法结构.函数式编程是一种编程范式 (而面向过程编程和面向对象编程也都是编程范式).在面向过程编程中,我们见到过函数(function):在面向 ...

  2. 理解Python闭包概念

    闭包并不只是一个python中的概念,在函数式编程语言中应用较为广泛.理解python中的闭包一方面是能够正确的使用闭包,另一方面可以好好体会和思考闭包的设计思想. 1.概念介绍 首先看一下维基上对闭 ...

  3. python中对 函数 闭包 的理解

    最近学到 函数 闭包的时候,似懂非懂.迷迷糊糊的样子,很是头疼,今天就特意查了下关于闭包的知识,现将我自己的理解分享如下! 一.python 闭包定义 首先,关于闭包,百度百科是这样解释的: 闭包是指 ...

  4. python 装饰器、内部函数、闭包简单理解

    python内部函数.闭包共同之处在于都是以函数作为参数传递到函数,不同之处在于返回与调用有所区别. 1.python内部函数 python内部函数示例: def test(*args): def a ...

  5. Python 闭包

    什么是闭包? 闭包(closure)是词法闭包(lexical closure)的简称.闭包不是新奇的概念,而是早在高级程序语言开始发展的年代就已产生. 对闭包的理解大致分为两类,将闭包视为函数或者是 ...

  6. Python闭包及装饰器

    Python闭包 先看一个例子: def outer(x): def inner(y): return x+y return innder add = outer(8) print add(6) 我们 ...

  7. Python 闭包小记

    闭包就是能够读取其他函数内部变量的函数.例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“.在本质上,闭包是将函数内部和函数外部连接起来 ...

  8. python闭包和装饰器

    本文目录: 1. 闭包的解析和用法 2. 函数式装饰器 3. 类装饰器 一.闭包 闭包是一种函数,从形式上来说是函数内部定义(嵌套)函数,实现函数的扩展.在开发过程中,考虑到兼容性和耦合度问题,如果想 ...

  9. javascript中重要概念-闭包-深入理解

    在上次的分享中javascript--函数参数与闭包--详解,对闭包的解释不够深入.本人经过一段时间的学习,对闭包的概念又有了新的理解.于是便把学习的过程整理成文章,一是为了加深自己闭包的理解,二是给 ...

随机推荐

  1. Linux下OpenSSL加密解密压缩文件(AES加密压缩文件)

    OpenSSL是一个开源的用以实现SSL协议的产品,它主要包括了三个部分:密码算法库.应用程序.SSL协议库.Openssl实现了SSL协议所需要的大多数算法.下面介绍使用Openssl进行文件的对称 ...

  2. @objc

    Swift 和 Objective-C 的互调这个话题很大,今天我们重点看看其中一个小的知识点:@objc的使用. 用法 在 Swift 代码中,使用@objc修饰后的类型,可以直接供 Objecti ...

  3. 【转载】 C#使用string.Join快速用特定字符串串联起数组

    在C#中有时候我们的数组元素需要通过一些特定的字符串串联起来,例如将整形Int数组通过逗号快速串联起来成为一个字符串,可以使用String.Join方法.或者一个字符串string类型数组所有元素快速 ...

  4. Hbuilder中配置cmd

    步骤: 1. ①运行——>外部工具——>外部工具 配置2.  新建一个名为cmd(随意命名)的外部工具: 名称:cmd 要执行的命令或文件:C:\Windows\System32\cmd. ...

  5. Linux Exploit系列之三 Off-By-One 漏洞 (基于栈)

    Off-By-One 漏洞 (基于栈) 原文地址:https://bbs.pediy.com/thread-216954.htm 什么是off by one? 将源字符串复制到目标缓冲区可能会导致of ...

  6. S5PV210 PWM

    定时器PWM输出 原理图 GPD0CON, R/W, Address = 0xE020_00A0 CON, R/W, Address = 0xE250_0008 相关文章:http://blog.cs ...

  7. centos7 配置yum源

    简单粗暴方法: 1.首先在本机上测试 ping www.baidu.com 是否通,不通的话配置网卡ip.dns等. 2.进入yum路径下: cd /etc/yum.repos.d/ 3.下载repo ...

  8. Computer Vision_33_SIFT:Robust scale-invariant feature matching for remote sensing image registration——2009

    此部分是计算机视觉部分,主要侧重在底层特征提取,视频分析,跟踪,目标检测和识别方面等方面.对于自己不太熟悉的领域比如摄像机标定和立体视觉,仅仅列出上google上引用次数比较多的文献.有一些刚刚出版的 ...

  9. Django之过滤器

    Django 过滤器   过滤器 描述 示例 upper 以大写方式输出 {{ user.name | upper }} add 给value加上一个数值 {{ user.age | add:”5” ...

  10. Python函数Day1

    一.函数的初识 函数的定义:函数最主要的目的是封装一个功能,一个函数就是一个功能 定义函数的格式: def 函数名(): 函数体 def my_len(): count = 0 s1 = 'hahah ...