Bootstrap

什么是Python中的闭包与装饰器

1.闭包

闭包(Closure)是指在一个函数内部定义的函数,并且这个内部函数可以访问其外部函数作用域中定义的变量。在 Python 中,闭包是一个强大且灵活的编程工具,可以实现许多有趣和实用的功能。

让我们通过一个简单的示例来说明闭包的基本概念:

def outer_function(x):
    def inner_function(y):  # 内部函数 
        return x + y 
    return inner_function # 外部函数返回内部函数的引用 
closure = outer_function(10) # 创建闭包 
result = closure(5) # 调用闭包 
print(result) # 输出 15

在这个例子中:

  • outer_function 是外部函数,接受一个参数 x
  • inner_function 是内部函数,它接受参数 y,并返回 x + y 的结果。
  • outer_function(10) 调用返回了 inner_function 的引用,并且在这个过程中 x 被设置为 10。
  • 我们将 outer_function(10) 的返回结果赋给 closure,这个 closure 就成为了一个闭包。
  • 当我们调用 closure(5) 时,实际上是在调用 inner_function,这时闭包中的 x 值为 10,所以结果为 10 + 5 = 15。

总结三点就是:闭包的形成一定是有外部函数嵌套内部函数、外部函数返回值为内部函数、内部函数可以访问外部函数的局部变量。

闭包在实际编程中有许多应用,例如:

  • 工厂函数:通过闭包可以动态生成函数,根据不同的输入参数生成不同的逻辑。
  • 装饰器:装饰器本质上就是闭包,它可以在不修改函数源代码的情况下,添加额外的功能。
  • 回调函数:将函数作为参数传递给其他函数,形成闭包,可以在异步编程中非常有用。

在使用闭包时,需要注意以下几点:

  • 变量绑定时间:闭包中的自由变量在定义时被绑定,而不是在调用时。
  • 内存管理:闭包会保留外部函数的环境,可能会导致内存占用问题,尤其是在闭包的作用域很大或者存在循环引用时。

2.装饰器

Python装饰器是一种高级功能,它允许在不改变函数本身代码的情况下,动态地修改或者增强函数的行为。装饰器本质上是一个函数,它接受一个函数作为输入,并返回一个新的函数作为输出。在函数执行之前或之后,装饰器允许添加额外的功能。

让我们通过一个简单的示例来说明装饰器的基本用法:

def decorator_function(func): # 定义一个装饰器函数 
    
    def wrapper(): 
        print("Wrapper executed this before {}".format(func.__name__)) 
        return func() # 执行原始函数 
    
    return wrapper


def say_hello():  # 定义一个普通函数 

    print("Hello!")  


say_hello = decorator_function(say_hello) # 使用装饰器来增强函数的功能
say_hello()# 调用经过装饰的函数 

在这个例子中:

  • decorator_function 是一个装饰器函数,它接受一个函数 func 作为参数。
  • wrapper 函数是装饰器内部定义的函数,它在调用被装饰的函数之前添加了额外的功能。
  • 最后,我们通过 say_hello = decorator_function(say_hello) 将 say_hello 函数应用了 decorator_function 装饰器。

可以看出,其实装饰器是合理运用了闭包的特性,在内部函数调用了外部函数的形参函数。在内部函数对调用的函数进行功能装饰,实现不改变原有函数功能的基础上增添了新的功能实现。并且,不单单是某个函数可以被内部函数调用进行装饰,还可以有很多个一起。

在上述例子中,还可以换一种专属于装饰器的写法。

def decorator_function(func): # 定义一个装饰器函数 
    
    def wrapper(): 
        print("Wrapper executed this before {}".format(func.__name__)) 
        return func() # 执行原始函数 
    
    return wrapper


@decorator_function  # 在函数前加一顶“帽子”,用@ 将 target_function 函数传递给 decorator 装饰器,并将返回的函数重新赋值给 target_function。
def say_hello():  # 定义一个普通函数 

    print("Hello!")  


say_hello()  # 调用经过装饰的函数 

可以看出,装饰器通过 @ 符号应用在函数定义之前,会将 target_function 函数传递给 decorator 装饰器,并将返回的函数重新赋值给 target_function。从而,每次调用 target_function 时,实际上是调用了经过装饰器处理后的函数。

装饰器的应用场景

  • 日志记录: 装饰器可用于记录函数的调用信息、参数和返回值。
  • 性能分析: 可以使用装饰器来测量函数的执行时间。
  • 权限控制: 装饰器可用于限制对某些函数的访问权限。
  • 缓存: 装饰器可用于实现函数结果的缓存,以提高性能。

通过装饰器,开发者可以在保持代码整洁的同时,灵活且高效地扩展程序的功能。

3.装饰器案例

 在几个案例中去了解一下装饰器的一些使用情况和方式。

user = None


def index():
    print("欢迎来到小宇家杂货铺")


def login_required(f):
    def check():
        global user
        if user:
            f()
        else:
            while True:
                username = input("请输入用户名")
                password = input("请输入密码")
                if username == "cxy" and password == "123456":
                    f()
                    user = {"username": username, "password": password}
                    break
                else:
                    print("用户名和密码错误")
    return check


@login_required
def go_center():
    print("进入个人中心成功")


@login_required
def go_cart():
    print("进入购物车成功")


@login_required
def go_search():
    print("进入搜索页成功")


index()
go_center()
go_cart()

上述案例是模仿在某购物平台进行的用户登录,无论在哪里进行用户登录,之后都可以通过登录数据跳过登录选项,也是运用装饰器的功能实现装饰三个需要相同功能的地方。

不仅如此,装饰器函数也可以接受参数。

def repeat(n):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(n):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")

以上代码中 repeat 函数是一个带参数的装饰器,它接受一个整数参数 n,然后返回一个装饰器函数。该装饰器函数内部定义了 wrapper 函数,在调用原始函数之前重复执行 n 次。因此,greet 函数在被 @repeat(3) 装饰后,会打印三次问候语。

Python 还允许将多个装饰器堆叠在一起,按照从上到下的顺序应用:

def decorator1(func): 
    
    def wrapper(): 
        print("Decorator 1 executed") 
        return func() 
    return wrapper 


def decorator2(func): 

    def wrapper(): 
        print("Decorator 2 executed") 
        return func() 
    return wrapper 


@decorator1 
@decorator2 
def say_hello(): 
print("Hello!") 


say_hello()

在这个例子中,先应用 @decorator2,再应用 @decorator1,最终调用 say_hello() 时会按照 Decorator 1 executedDecorator 2 executed 的顺序输出。

最后运用装饰器来实现对比两个函数的执行效率。

import random
import time

datas = [random.randint(0, 10000) for i in range(10000)]
# 浅拷贝一个数据,得到两个一样的值
data = datas.copy()


# 定义一个时间耗费函数装饰器
def time_coast(f):

    def calc():
        start = time.time()
        f()
        print(time.time() - start)
    return calc


# 对函数一进行装饰
@time_coast
def fun1():
    # 对数据进行方法一排序
    datas.sort()
    print(datas)


# 对函数二进行装饰
@time_coast
def fun2():
    # 对浅拷贝出来的数据进行方式二排序
    new_data = sorted(data)
    print(new_data)


# 执行装饰后的函数,比较两个函数花费时间那个少
fun1()
fun2()

 可以看出,装饰器的调用,实现了查看两函数的执行耗费时间,但是没有改变原有函数的功能。装饰器的存在,让我们在编程时,可以给函数添加更多更丰富的功能的同时,又不会让我们的代码看起来繁琐杂乱。

 

;