假设我们需要做一个题目,是求y=kx+b
这个一元一次函数在多个x上的值。
1.前言
第一种方法:
# -*- coding: utf-8 -*-
# @Author : summer
k = 1
b = 2
x = 0
print(k * x + b)
x = 1
print(k * x + b)
x = 2
print(k * x + b)
这个是最平常的一种写法,即直接定义一个k和一个b,然后写一个x,接着直接打印出y的值
其实这个方法的缺点也可以看出–》代码冗余。那么代码冗余的话我们可不可以用函数解决呢?
第二种:函数法
# -*- coding: utf-8 -*-
# @Author : summer
k = 1
b = 2
def create(x):
print(k * x + b)
create(1)
函数的方法虽然可以,但是如果代码过多,或者在create函数后面还有许多的函数,就可能导致全局变量k和b被修改导致结果出错,那有没有什么办法可以把变量存起来,让自己单独使用呢?没错就是面向对象方法
第三种:面向对象法
# -*- coding: utf-8 -*-
# @Author : summer
class Create:
def __init__(self, k, b):
self.k = k
self.b = b
def __call__(self, x, *args, **kwargs):
print(self.k * x + self.b)
c1 = Create(1, 2)
c1(1)
同样,上面的方法虽然可以把全局变量放入自己内部,但别忘了,创建对象时不知有现在所看到的两个魔法方法,每个对象默认调用object,因此你创建一个对象就会多出好多个没有的魔法方法,因此还有没有更好的方法呢?他来了—-闭包
第四种:闭包
# -*- coding: utf-8 -*-
# @Author : summer
def test(k, b):
def create(x):
print(k * x + b)
return create
t = test(1, 2)
t(1)
上面这种方法就是闭包,即函数里面再嵌套一层函数,并且最外层的函数返回值时内层函数的引用
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))
运行结果:
in test_in 函数, number_in is 100
120
in test_in 函数, number_in is 200
220
3.看一个闭包的实际例子:
def line_conf(a, b):
def line(x):
return a*x + b
return line
line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5))
print(line2(5))
这个例子中,函数line与变量a,b构成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个变量的取值,这样,我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。
如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。
注意点:
由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存
4.修改外部函数中的变量
def counter(start=0):
def incr():
nonlocal start
start += 1
return start
return incr
c1 = counter(5)
print(c1())
print(c1())
c2 = counter(50)
print(c2())
print(c2())
print(c1())
print(c1())
print(c2())
print(c2())
这里使用一个nonlocal来声明start。和global差不多就是作用范围不同。
5.思考闭包与其他函数的区别
1.匿名函数能够完成基本的简单功能。传递的是这个参数的引用因此只有功能。
2.普通函数能够完成较为复杂的功能。传递的是这个函数的引用因此也只有功能。
3.闭包能后完成较为复杂的功能。传递的是这个闭包中的函数以及数据因此传递的是功能和数据。
4.对象能后完成最复杂的功能。传递的是很多数据+功能因此传递的是功能和数据。