8: 闭包
Python 中的闭包是通过函数调用创建的。在这里,对 makeInc 的调用为 x 创建了一个绑定,该绑定在函数 inc 中被引用。对 makeInc 的每次调用都会创建该函数的一个新实例,但每个实例都有一个指向 x 的不同绑定的链接。
def makeInc(x):
def inc(y):
# x is "attached" in the definition of inc
return y + x
return inc
incOne = makeInc(1)
incFive = makeInc(5)
incOne(5) # returns 6
incFive(5) # returns 10
请注意,在普通闭包中,被括弧函数完全继承其外层环境中的所有变量,而在这种结构中,被括弧函数对继承变量只有读取权限,而不能对它们赋值
def makeInc(x):
def inc(y):
# incrementing x is not allowed
x += y
return x
return inc
incOne = makeInc(1)
incOne(5) # UnboundLocalError: local variable 'x' referenced before assignment
Python 3 提供了非局部语句(非局部变量),用于实现嵌套函数的完全闭包。
def makeInc(x):
def inc(y):
nonlocal x
# now assigning a value to x is allowed
x += y
return x
return inc
incOne = makeInc(1)
incOne(5) # returns 6
9: 强制使用命名参数
在函数签名中第一个星号之后指定的所有参数都只有关键字。
def f(*a, b):
pass
f(1, 2, 3)
# TypeError: f() missing 1 required keyword-only argument: 'b'
在 Python 3 中,可以在函数签名中加上一个星号,以确保其余参数只能使用关键字参数传递。
def f(a, b, *, c):
pass
f(1, 2, 3)
# TypeError: f() takes 2 positional arguments but 3 were given
f(1, 2, c=3)
# 没有报错
10: 嵌套函数
python 中的函数是一级对象。它们可以在任何作用域中定义
def fibonacci(n):
def step(a,b):
return b, a+b
a, b = 0, 1
for i in range(n):
a, b = step(a, b)
return a
函数捕获其外层作用域,可以像任何其他类型的对象一样进行传递
def make_adder(n):
def adder(x):
return n + x
return adder
add5 = make_adder(5)
add6 = make_adder(6)
add5(10)
#Out: 15
add6(10)
#Out: 16
def repeatedly_apply(func, n, x):
for i in range(n):
x = func(x)
return x
repeatedly_apply(add5, 5, 1)
#Out: 26
11: 递归限制
递归的深度是有限制的,这取决于 Python 的实现。当达到限制时,将引发 RuntimeError 异常:
def cursing(depth):
try:
cursing(depth + 1) # actually, re-cursing
except RuntimeError as RE:
print('I recursed {} times!'.format(depth))
cursing(0)
# Out: I recursed 1083 times!
可以使用 sys.setrecursionlimit(limit) 更改递归深度限制,并通过 sys.getrecursionlimit() 检查该限制。
import sys
sys.setrecursionlimit(2000)
cursing(0)
# Out: I recursed 1997 times!
从 Python 3.5 开始,异常是一个从 RuntimeError 派生的 RecursionError。
12: 使用分配变量的递归 Lambda
创建递归 lambda 函数的一种方法是将函数赋值给一个变量,然后在函数本身中引用该变量。一个常见的例子是递归计算一个数字的阶乘,如以下代码所示:
lambda_factorial = lambda i:1 if i==0 else i*lambda_factorial(i-1)
print(lambda_factorial(4)) # 4 * 3 * 2 * 1 = 12 * 2 = 24
代码说明
lambda 函数通过变量赋值传递一个值 (4),并对其进行求值,如果值为 0 则返回 1,否则返回当前值 (i) * lambda 函数对值 - 1 (i-1)的另一次计算。这个过程一直持续到传递的值减为 0(return 1)为止。这个过程可以形象地描述为