[Python]闭包的理解和使用

您所在的位置:网站首页 python中的闭包函数 [Python]闭包的理解和使用

[Python]闭包的理解和使用

2023-08-28 12:05| 来源: 网络整理| 查看: 265

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

闭包是什么?

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

举个例子

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

一般支持将函数当做对象使用的编程语言,如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__) #(,) for x in square.__closure__: print(x.cell_contents) #2 print(cube.__closure__) for x in cube.__closure__: #(,) print(x.cell_contents) #3

参考资料: 廖雪峰 Python装饰器 Python中的闭包



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3