【python进阶 笔记】闭包

本文从一个实际问题引入,详细解释了Python闭包的概念,包括它如何提高代码复用性和内存消耗的特点。此外,还探讨了闭包与函数、匿名函数、对象的区别,并介绍了如何在闭包中使用`nonlocal`关键字修改外部函数的变量。
摘要由CSDN通过智能技术生成

【python进阶 笔记】闭包

目录

1. 问题引入

2. 什么是闭包?(重点)

3. 修改外部函数中的变量


先通过一个问题的解决来引入闭包,再对闭包进行解释和执行过程分析。

1. 问题引入

以y=kx+b为例,请计算一条线上的多个点 即 给x值 计算出y值。

# 问题:初中里学过函数,例如 y=kx+b, y=ax^2 + bx + c

# 以y=kx+b为例,请计算一条线上的多个点 即 给x值 计算出y值

# 第1种
# k = 1
# b = 2
# y = k*x+b
# 缺点:如果需要多次计算,那么就的写多次y = k*x+b这样的式子,不能重用

# 第2种:定义一个函数
def line_2(k, b, x):
	print(k*x+b)

line_2(1, 2, 0)
line_2(1, 2, 1)
line_2(1, 2, 2)
# 缺点:如果想要计算多次这条线上的y值,那么每次都需要传递k,b的值,麻烦

print("-"*50)


# 第3种: 全局变量,
k = 1
b = 2
def line_3(x):
	print(k*x+b)

line_3(0)
line_3(1)
line_3(2)
k = 11
b = 22
line_3(0)
line_3(1)
line_3(2)
# 缺点:如果要计算多条线上的y值,那么需要每次对全局变量进行修改,代码会增多,麻烦

print("-"*50)

# 第4种:缺省参数(注意,缺省参数放在后面)
def line_4(x, k=1, b=2):
	print(k*x+b)

line_4(0)
line_4(1)
line_4(2)

line_4(0, k=11, b=22)
line_4(1, k=11, b=22)
line_4(2, k=11, b=22)
# 优点:比全局变量的方式好在:k, b是函数line_4的一部分 而不是全局变量,因为全局变量可以任意的被其他函数所修改
# 缺点:如果要计算多条线上的y值,那么需要在调用的时候进行传递参数,麻烦

print("-"*50)

# 第5种:实例对象
# 创建实例对象的时候设置好k和b
class Line5(object):
	def __init__(self, k, b):
		self.k = k
		self.b = b

	def __call__(self, x):  # __call__方法,对象后面加括号,触发执行
		print(self.k * x + self.b)


line_5_1 = Line5(1, 2)
# 对象.方法()
# 对象():会触发__call__方法
line_5_1(0)  # 该形式即 对象() ,会调用__call__ 
line_5_1(1)
line_5_1(2)
line_5_2 = Line5(11, 22)
line_5_2(0)
line_5_2(1)
line_5_2(2)
# 缺点:为了计算多条线上的y值,所以需要保存多个k, b的值,因此用了很多个实例对象, 浪费资源

print("-"*50)

# 第6种:闭包

def line_6(k, b):
	def create_y(x):
		print(k*x+b)
	return create_y

line_6_1 = line_6(1, 2)  # 
line_6_1(0)
line_6_1(1)
line_6_1(2)
line_6_2 = line_6(11, 22)
line_6_2(0)
line_6_2(1)
line_6_2(2)

在第六种,闭包的解法中:

函数create_y与变量k,b构成闭包。在创建闭包的时候,我们通过 line_6 的参数k,b说明了这两个变量的取值。这样确定了函数的最终形式(y = kx + b)。我们只需要变换参数k,b,就可以获得不同的直线表达函数。可见,闭包也具有提高代码可复用性的作用。如果没有闭包,则需要每次创建直线函数的时候同时说明k,b,x。需要更多的参数传递,减少了代码的可移植性。

( 注意 :由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,会消耗内存)

 

思考:函数、匿名函数、闭包、对象 做实参时, 有什么区别?

  1. 匿名函数能够完成基本的简单功能,传递是这个函数的引用 只有功能(注:匿名函数由lambda关键字创建,冒号后面的表达式有且只能有一个);
  2. 普通函数能够完成较为复杂的功能,传递是这个函数的引用 只有功能;
  3. 闭包能够完成较为复杂的功能,传递是这个闭包中的函数以及数据,因此传递是 功能+数据(较简单的);
  4. 对象能够完成最为复杂的功能,传递是很多数据+很多功能(许多属性和方法可以用),因此传递是 功能+数据;

 

2. 什么是闭包?(重点)

函数内部再定义一个函数(多层函数嵌套定义),并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包。

# 定义一个函数
def test(number):

    # 在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包
    def test_in(number_in):
        print("in test_in 函数, number_in is %d" % number_in)
        return number+number_in
    # 其实这里返回的就是闭包的结果
    return test_in


# 给test函数赋值,这个20就是给参数number
ret = test(20)

# 注意这里的100其实给参数number_in
print(ret(100))

#注 意这里的200其实给参数number_in
print(ret(200))

执行过程:

  1. ret = test(20): 执行test函数,def test_in 语句定义函数直接跳过不执行 ,return test_in 返回函数的引用;
  2. ret 此时指向 函数test_in ,执行 ret(100) 意味着调用函数 test_in ,传参为 100 。

 

3. 修改外部函数中的变量

在局部中修改全局变量可以通过 global 关键字来修改,在闭包中该如何修改外部函数的变量呢?

  • 闭包中使用 nonlocal 修改外部函数的变量。

  • 闭包和外部函数有同名的变量时 ,闭包中加上 nonlocal 才会访问到外部函数的变量。

demo1

def counter(start=0):
    def incr():
        nonlocal start
        start += 1
        return start
    return incr

c1 = counter(5)
print(c1())  # 6
print(c1())  # 7

c2 = counter(50)
print(c2())  # 51
print(c2())  # 52

print(c1())  # 8
print(c1())  # 9

print(c2())  # 53
print(c2())  # 54

 

 

demo2

x = 300
def test1():
	x = 200
	def test2():
		nonlocal x  # 因为下面定义了局部的x,不写这句的话下面的输出语句会一位x是局部变量的x,报错
		print("----1----x=%d" % x)
		x = 100
		print("----2----x=%d" % x)
	return test2

t1 = test1()
t1()

输出:

----1----x=200
----2----x=100

注:以上nonlocal方法不适用与python2

 

 

-----end-----

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值