1. 闭包初探
在Python中,函数是一等公民,因此可以像其他对象一样进行传递、赋值等操作。其中,闭包(Closure)是一种比较特殊的函数,它可以捕获自由变量的值,将函数与其所在作用域中的变量绑定在一起,形成一个闭合的函数。函数处理的不仅仅是函数体内的局部变量,还包括外部环境中定义的变量。简单来说,闭包是一种由函数及其引用环境组合而成的实体(即函数加上它所处的环境),可以理解为函数的内部函数。
1.1 闭包的构成
闭包由两部分组成:
函数(即内部函数,或者称为嵌套函数)
与内部函数相关的引用环境(Closure)
那么,什么是引用环境呢?它实际上是一个保存了外部变量名到对象之间映射关系的字典。字典的键是变量名,而对应的键值是变量所对应的对象。这个字典会在每次内部函数执行时被传递给内部函数。因为内部函数可以访问该字典,所以它就可以使用该字典中的值。
我们可以通过一个示例代码来展示闭包的构成:
def outer_func(x):
def inner_func(y):
return x * y
return inner_func
f = outer_func(10)
print(f(5)) # 50
这个软件包含两个函数:outer_func
和inner_func
。外部函数outer_func
返回内部函数inner_func
的引用,即f
。因此,f
是一个闭包,可以访问由x
构成的引用环境。在代码中,我们用f(5)
调用内部函数,它会返回10*5=50
。
1.2 闭包的特点
闭包在技术上不会产生新的功能,但在实际使用中,它有以下几种特点:
在外部作用域中定义了一个变量,闭包可以根据需要改变这个变量,而在外部作用域中,这个变量的值不会改变。
闭包可以访问外部函数的变量,并持久化这些变量。
下面的示例代码描述了这两个特点:
def outer_func():
count = 0
def inner_func():
nonlocal count
count += 1
return count
return inner_func
f1 = outer_func()
f2 = outer_func()
print(f1()) # 1
print(f1()) # 2
print(f2()) # 1
print(f2()) # 2
在这个例子中,我们首先定义了外部函数outer_func
,它初始化计数器为0
。内部函数inner_func
会读取和更新这个计数器,然后返回计数器的值。再之后,我们定义了两个闭包f1
和f2
,它们分别引用内部函数inner_func
,因此它们各自具有一个独立的计数器。最后,我们依次调用f1
和f2
,它们的计数器都能够独立地更新和返回。
2. 装饰器初步
Python中的装饰器(Decorator)是一种与闭包密切相关的概念。它实际上是一种用于修改其他函数的函数,它的本质就是一个闭包。一个装饰器本质上是一个可调用对象,它接受一个函数并返回另一个函数。这个过程中,装饰器可以添加、修改或者删除原函数的功能,而本身并不会修改原函数的定义。
2.1 装饰器的语法
装饰器在Python中的语法非常简洁,只需要在函数前面添加一个“@装饰器函数名”的注解,即可将装饰器应用到原函数上。下面的代码演示了如何使用装饰器:
def decorator_func(func):
def wrapper():
print("Before calling the function.")
func()
print("After calling the function.")
return wrapper
@decorator_func
def original_func():
print("The original function is called.")
original_func()
在这个例子中,我们首先定义了一个装饰器函数decorator_func
,它接受一个函数作为参数,并返回一个包装了原函数的新函数。在原函数定义前面,我们使用@decorator_func
的形式将装饰器应用到了原函数上。这意味着当我们调用original_func
时,实际上会执行由装饰器返回的包装函数wrapper
。由于在包装函数中,我们在调用原函数之前和之后分别打印了一些额外的信息,因此最终输出的内容会包含这些额外的信息。
2.2 多个装饰器的应用
装饰器还可以组合使用,形成多个装饰器的链式结构。这种链式的结构实际上是多个装饰器产生的闭包中函数调用的结果。在多个装饰器的链式结构中,每个装饰器都是在原函数的基础上进行修改,最终形成多个嵌套的函数调用。这个过程中,每个装饰器都可以改变原函数的状态,让原函数获得更多的功能。
下面的代码演示了如何使用多个装饰器:
def decorator1(func):
def wrapper(*args, **kwargs):
print("Decorator1 is called.")
return func(*args, **kwargs)
return wrapper
def decorator2(func):
def wrapper(*args, **kwargs):
print("Decorator2 is called.")
return func(*args, **kwargs)
return wrapper
@decorator1
@decorator2
def original_func(a, b):
print("The original function is called with argument a={} and b={}.".format(a, b))
original_func(1, 2)
在这个例子中,我们定义了两个装饰器函数decorator1
和decorator2
。这两个装饰器的功能都是在原函数调用前后打印一些额外的信息。在原函数定义前面,我们使用@decorator1
、@decorator2
的形式将两个装饰器依次应用到原函数上。因此,最终调用original_func
时,实际上会先调用decorator1
返回的装饰函数,再调用decorator2
返回的装饰函数,最终执行原函数。在执行过程中,原函数的参数会依次传递给这两个装饰器函数。