1. 函数
python中的一切皆对象,函数也是一个对象,如下:
In [44]: def test():
....: pass
....:
In [45]: type(test)
Out[45]: function
In [46]: type(test.__call__)
Out[46]: method-wrapper
如果一个对象能使用函数调用的方式调用(即xxx()的方式),那么这个对象需要定义一个__call__的魔法属性,如下:
In [47]: class Foo(object):
....: def __call__(self):
....: print("__call__")
....:
In [48]: f = Foo()
In [49]: f()
__call__
在Foo对象中定义了一个__call__的魔法属性,创建了Foo的一个实现对象f,并使用调用:f(),如果传递一个函数对象的引用,那么通过这个函数的应用就能调用这个函数。
2. 什么是闭包?
假如要计算反正y=ax+b的值,有如下几种做法:
2.1 直接计算
# 第1种
a = 1
b = 2
y = k*x+b
缺点:如果需要多次计算,那么就的写多次y = a*x+b这样的式子
2.2 使用函数
# 2. 使用函数
def test(a, b, x):
print(a * x + b)
test(1, 2, 1)
test(1, 2, 10)
test(2, 4, 1)
test(2, 4, 10)
缺点: 如果要多次计算某条y=ax+b上的值,需要多次传入a,b的值
2.3 使用全局变量
# 3. 使用全局变量
a = 1
b = 2
def test(x):
print(a * x + b)
test(1)
test(10)
a = 2
b = 4
test(1)
test(10)
缺点:如果要计算多条y=ax+b上的值,需要多次修改全局变量a,b的值,麻烦
2.4 使用缺省参数
# 4. 使用缺省参数
def test(x, a=1, b=2):
print(a * x + b)
test(1)
test(10)
test(1, a=2, b=4)
test(10, a=2, b=4)
优点:相比第三种方式,a和b是默认参数的一部分,不是全局变量
缺点:如果计算多条y=ax+b的值,需要多次传入a,b的值麻烦
2.5 使用实例对象
# 5. 使用实例对象
class Test(object):
def __init__(self, a, b):
self.a = a
self.b = b
def __call__(self, x):
print(self.a * x + self.b)
t1_2 = Test(1, 2)
t1_2(1)
t1_2(10)
t2_4 = Test(2, 4)
t2_4(1)
t2_4(10)
缺点,为了计算y的值,需要保存a,b的值,用了实例对象,浪费资源
2.6 使用闭包
# 6. 使用闭包
def test(a, b):
def create_y(x):
print(a * x + b)
return create_y
t1_2 = test(1, 2)
t1_2(1)
t1_2(10)
t2_4 = test(2, 4)
t2_4(1)
t2_4(10)
函数create_y与变量a,b构成闭包。在创建闭包的时候,我们通过test的参数a,b说明了这两个变量的取值,这样,我们就确定了函数的最终形式(y = x + 2和y = 2x + 4)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。
如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。
关于闭包,维基百科的定义如下:
在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。
百度百科:
闭包是可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。“闭包” 一词来源于以下两者的结合:要执行的代码块(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)和为自由变量提供绑定的计算环境(作用域)。在 Scala、Scheme、Common Lisp、Smalltalk、Groovy、JavaScript、Ruby、 Python和Lua,objective c 等语言中都能找到对闭包不同程度的支持。
在编程领域我们可以通俗的说:子函数可以使用父函数中的局部变量,这种行为就叫做闭包!
3. 修改闭包中的数据
python3中:
x = 100
def test1():
x = 101
def test2():
nonlocal x
print("--1--x: %s" % x)
x = 102
print("--2--x: %s" % x)
return test2
test1()()
像在函数中修改全局变量一样,在python3中修改闭包中的数据需要使用nonlocal
,就像上面那么:nonlocal x
python2中:
def test1():
x = 101
temp_list = [x]
def test2():
print("--1--x: %s" % temp_list[0])
temp_list[0] = 102
print("--2--x: %s" % temp_list[0])
return test2
test1()()
由于python2没有像python3中的nonlocal
,要在python2中修改闭包中的数据,只能在一个list中保存要修改的变量,直接对该列表中对应的数据进行修改就能完成伪修改。