Python-闭包与装饰器

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_funcinner_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会读取和更新这个计数器,然后返回计数器的值。再之后,我们定义了两个闭包f1f2,它们分别引用内部函数inner_func,因此它们各自具有一个独立的计数器。最后,我们依次调用f1f2,它们的计数器都能够独立地更新和返回。

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)

在这个例子中,我们定义了两个装饰器函数decorator1decorator2。这两个装饰器的功能都是在原函数调用前后打印一些额外的信息。在原函数定义前面,我们使用@decorator1@decorator2的形式将两个装饰器依次应用到原函数上。因此,最终调用original_func时,实际上会先调用decorator1返回的装饰函数,再调用decorator2返回的装饰函数,最终执行原函数。在执行过程中,原函数的参数会依次传递给这两个装饰器函数。

后端开发标签