[Python学习篇] Python闭包和装饰器

目录

闭包

什么是闭包

闭包的特性

装饰器

什么是装饰器

装饰器的特性

装饰器常用的应用场景

装饰器代码

无参数的装饰器

装饰器语法糖

带任意参数的装饰器

可组合性装饰器

装饰器工厂

类装饰器


闭包

什么是闭包

Python 闭包(Closure)是指在一个内嵌函数中引用了外部作用域的变量,并且这个内嵌函数本身作为返回值的一种函数结构。换句话说,闭包使得内嵌函数可以“记住”它所在的外部函数的作用域中的变量,即使外部函数已经执行完毕。这在某些情况下非常有用,因为它允许函数携带状态。

闭包的特性

闭包的特性或者叫做闭包必须遵循的规范。

  1. 嵌套函数:闭包必须包含一个内嵌函数。(嵌套函数是函数内部还有函数)。
  2. 引用外部作用域的变量:内嵌函数必须引用包含它的外部函数中的变量。
  3. 外部函数返回内嵌函数:外部函数必须返回这个内嵌函数。
  • 创建闭包时,传入的参数是外部函数的参数。
  • 使用闭包时,传入的参数是内部函数的参数。

示例:

这个示例完全解释闭包的特性。

def out_fun(x):     # 外部函数
    def in_fun(y):  # 内嵌函数
        return x + y

    return in_fun   # 外部函数的返回值是内嵌函数


# 创建一个闭包
closure = out_fun(10)   # 这里的参数10,传给的是外部函数的

# 使用闭包
print(closure(5))  # 输出15,这里的参数5,传给的是内部函数的

示例:

修改外部变量的值

nonlocal 是 Python 中的一个关键字,用于在一个内嵌函数中声明一个变量来自于其外部作用域(但不是全局作用域),并且允许内嵌函数修改该变量的值。

def make_counter():
    count = 0

    def counter():
        nonlocal count
        count += 1
        return count

    return counter


# 创建计数器闭包
counter1 = make_counter()
print(counter1())  # 输出: 1
print(counter1())  # 输出: 2

counter2 = make_counter()
print(counter2())  # 输出: 1
print(counter2())  # 输出: 2
print(counter2())  # 输出: 3
print(counter1())  # 输出: 3

装饰器

什么是装饰器

Python 装饰器(Decorator)是一种高级函数,用于在不修改已有函数或方法定义的前提下,动态地添加功能。装饰器本质上是一个高阶函数,它接受一个函数作为输入并返回一个新的函数。这种特性使得装饰器非常适合用于代码重用和分离关注点(Separation of Concerns)。

类比Java中的AOP动态代理。

装饰器的特性

  • 高阶函数:装饰器是接受一个函数作为参数并返回一个新函数的高阶函数。意思是:闭包的参数是有且只有一个的参数且参数类型是函数的闭包,这样的闭包就是装饰器。

  • 可组合性:多个装饰器可以通过堆叠的方式组合使用,每个装饰器依次作用于被装饰的函数。

  • 闭包:装饰器通常使用闭包来保持对被装饰函数的引用,并在内部函数中访问和修改装饰器的参数或状态。

  • 参数传递:装饰器可以处理任意数量的位置参数和关键字参数,适应不同类型的函数。

  • 装饰器工厂:装饰器可以通过工厂函数生成,允许装饰器接收参数,进一步增强灵活性。

  • 保持函数签名:使用 functools.wraps 装饰器可以保持被装饰函数的原始签名和文档字符串,使得装饰后的函数更具可读性。

  • 类装饰器:装饰器不仅可以用于函数,还可以用于类,为类添加或修改行为。

装饰器常用的应用场景

  • 日志记录:在函数执行前后记录日志信息。
  • 权限验证:在执行函数前检查用户权限。
  • 缓存:缓存函数的结果以提高性能。
  • 输入验证:在函数执行前验证参数是否合法。

装饰器代码

无参数的装饰器

示例:

需求:原本有一个查询详细信息的函数。在不改动这个函数的情况下给这个函数加前置后置功能。

# 定义装饰器函数
def out_fun(fun):
    def in_fun():
        print('装饰器前置功能:验证登录信息')  # 前置功能
        fun()
        print('装饰器后置功能:查询结果存入缓存')  # 后置功能

    return in_fun


# 普通函数
def get_info():
    print("原函数功能:查询详细信息")


# 使用装饰器
# *** 参数:装饰器函数参数传入普通函数,表示这个函数是要被装饰的函数。
# *** 返回值:装饰器函数返回名称可以任意定义,但是为了和我们普通函数保持同样的调用,这里的返回名称定义的和普通函数名称相同。
get_info = out_fun(get_info)

# 这个时候再使用原本的普通函数,已经被装饰了
# 未装饰前调用的是原来的普通函数,装饰后调用的是装饰器函数返回的同名函数
get_info()

装饰器语法糖

示例:

对上一个示例,使用语法糖的写法,使代码更加简洁。

装饰器通常通过 `@装饰器函数名称` 的语法糖形式应用于函数或方法上。

# 定义装饰器函数
def out_fun(fun):
    def in_fun():
        print('装饰器前置功能:验证登录信息')  # 前置功能
        fun()
        print('装饰器后置功能:查询结果存入缓存')  # 后置功能

    return in_fun


@out_fun  # 使用装饰器函数,装饰器函数名称叫什么,咱们这里语法糖就写什么
def get_info():  # 普通函数
    print("原函数功能:查询详细信息")


# 使用被装饰过的普通函数
get_info()
带任意参数的装饰器

任意参数个数的装饰器

# 定义一个装饰器函数
def out_func(func):
    def in_func(*args, **kwargs):
        print("前置传入的参数:", args, kwargs)
        f = func(*args, **kwargs)
        print("后置")
        return f

    return in_func


@out_func  # 使用装饰器函数
def add(a, b):
    print("普通函数")
    return a + b


result = add(5, 3)
print("结果:", result)

可组合性装饰器

多个装饰器组合使用

def decorator1(func):
    def wrapper(*args, **kwargs):
        print("装饰器 1")
        return func(*args, **kwargs)

    return wrapper


def decorator2(func):
    def wrapper(*args, **kwargs):
        print("装饰器 2")
        return func(*args, **kwargs)

    return wrapper


# 创建装饰器
@decorator1
@decorator2
def say_hello():
    print("Hello!")


# 使用被装饰过的方法
say_hello()
装饰器工厂

语法是语法糖上也可以带参数。作用是使前置后置可以使用参数做一些条件查询判断等操作。

def judge(tag):
    def decorator_judge(func):
        def wrapper(*args, **kwargs):
            print("工厂参数是:", tag)
            result = func(*args, **kwargs)
            return result

        return wrapper

    return decorator_judge


# 使用装饰器
@judge(tag=1)
def greet(name):
    print(f"Hello {name}")


# 调用被装饰过的方法
greet("Tom")

类装饰器

作用:不改动类的情况下,给类添加或修改属性方法等。

# 定义类装饰器
def class_decorator(cls):
    # 为类添加属性
    cls.age = "18"

    # 为类添加方法
    def new_method(self):
        return "新方法"

    cls.new_method = new_method
    return cls


# 使用装饰器
@class_decorator
class MyClass:
    name = "Tom"


# 使用被装饰过的类
print(MyClass.age)  # 输出:18
obj = MyClass()
print(obj.new_method())  # 输出:新方法

  • 17
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

又逢乱世

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值