Python闭包的理解

什么是闭包?大多数人对它的印象就是函数中嵌套函数吧。本篇就来详细的介绍闭包及其作用。

首先要从初中所学的一个直线方程y = kx + b讲起了……
对于给定的k,b,y 会随着x的改变而改变,那如何用编程实现呢?
学会函数的你一开始是这样写的:

def line(k, b, x):
    return k * x + b

line(1, 2, x=1)
line(1, 2, x=2)
line(1, 2, x=3)

但这种方式是有一个很大的缺点,对于给定的k, b,我只想改变x的值,这种情况下就没必要反复传递k和b了
好,学会类的你又开始这么写了:

class Line(object):
    def __init__(self, k, b):
        self.k = k
        self.b = b

    def __call__(self, x, *args, **kwargs):
        return self.k * x + self.b


line = Line(1, 2)
print(line(1))
print(line(2))
print(line(3))

这种实现方式就明显比函数来的方便明了。但还是有问题:
对于不同的k, b,需要生成多个实例对象,太占用资源。(实例对象要等到程序结束才释放)
那有没有一种更轻量级的实现方式呢? 有,就是闭包

def line(k, b):
    def closure(x):
        return k * x + b
    return closure


line = line(1, 2)
print(line(1))
print(line(2))
print(line(3))

以上就是一个简单的闭包。解释一下闭包的概念:

内部函数用到了外部函数的变量,那么就将内部函数的代码与外部函数的变量统一封装在一起,然后返回内层函数的情况, 这就是闭包。

闭包的调用与类相似, 所以闭包也可以理解为一种“轻量级的对象

这里line函数返回的是一个内部函数对象,因为Python中,函数名也作为对象,可以作为line函数的返回值。
所以line变量(注意line不再是函数)指向了closure这个对象的地址,调用line(1)实际上就调用了closure(1)
函数在调用完即会释放,但如果内函数引用到了外部函数的变量,这些变量是不会释放的。
恰当地使用闭包能极大提高程序的运行与开发效率。那使用闭包还有哪些好处呢?

  1. 对于参数是不可变对象时,可以有效避免使用全局变量, 如下代码所示:
def f(x):
    def closure():
        nonlocal x
        x = x + 1
    closure()
    print("修改后的 x =", x)

f(1)

修改外部的不可变对象时,需要用nonlocal关键字指明该变量是外部变量。将x重新指向了计算后的结果2
这样把x封装在函数内部也说明了xf()的特有变量,就好比是类的实例变量。

  1. 而对于参数是可变对象时,使用闭包可以保留之前运行结果的状态
def move(pos):
    def closure(dir, step):
        pos[0] = pos[0] + dir[0] * step
        pos[1] = pos[1] + dir[1] * step
        return pos
    return closure


move = move([0, 0])  # 起始点(0, 0)
print(move((1, 0), 1))  # 先向x轴正方向移动一步
print(move((0, 1), 2))  # 再向y轴正方向移动两步
print(move((-1, 0), 2))  # 最后向x轴负方向移动两步

得到运行结果:

[1, 0] 
[1, 2]
[-1, 2]

以上就是模拟从0,0点朝上下左右不断移动的过程。

  1. 最后当参数是一个函数对象时,就是装饰器
import time

def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        value = func(*args, **kwargs)
        print("函数消耗时间为:", time.time() - start)
        return value
    return wrapper


def run(x):
    time.sleep(2)
    return x

run = timer(run)
print(run(2))

如上就是一个计算函数消耗时间的装饰器:在不修改原函数的代码与调用方式的前提下,增加原函数的功能
这里不过多的介绍装饰器,总之学好闭包是掌握装饰器的前提。
看到这你也大致了解闭包的用处了。

本篇结束。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值