当前位置: 首页 > news >正文

【Python】装饰器相关知识点

1. 闭包基础

知识点:

  • 闭包:由内外函数构成。外函数返回内函数,而内函数引用了外函数的变量,从而实现“闭包”效果,保证局部变量在函数调用结束后依然被保存。

代码示例:

def outer():
    x = 500  # 外函数变量
    def inner():
        y = 200
        return x + y  # 内函数引用了外部变量 x
    return inner  # 返回内函数本身,而非调用

# 使用闭包
func = outer()
print("闭包返回结果:", func())

函数实现装饰器

2. 装饰器基础

知识点:

  • 装饰器:本质上是一种闭包,用于在不修改原函数代码的前提下,动态地为函数添加额外功能。下面示例中定义了一个 costTime 装饰器,用于统计函数执行时间。

代码示例:

import time

def costTime(func):
    print("执行了costTime函数")  # 装饰器在装饰时会执行一次
    def inner(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)  # 调用原函数
        end = time.time()
        print(f"执行{func.__name__}花了{end - start}s")
        return result
    return inner

@costTime  # 等价于 add = costTime(add)
def add(a, b):
    time.sleep(1)  # 模拟耗时操作
    return a + b

print("add(1, 2) =", add(1, 2))

注意: 使用装饰器后,原函数 add 实际上变成了 inner 函数,元数据(如函数名)会发生变化。


3. 保留原函数元数据

知识点:

  • 为了避免装饰器覆盖原函数的元数据(例如 __name____doc__),可以使用 functools.wraps 装饰内层函数,使得原函数的元数据得以保留。

代码示例:

import time
from functools import wraps

def costTime(func):
    print("执行了costTime函数")
    
    @wraps(func)
    def inner(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"执行{func.__name__}花了{end - start}s")
        return result
    return inner

@costTime
def add(a, b):
    """
    求两个数的和
    """
    time.sleep(1)
    return a + b

print("add(1, 2) =", add(1, 2))
print("函数名称:", add.__name__)
print("函数文档:", add.__doc__)

4. 带参数的装饰器

知识点:

  • 带参数的装饰器:需要使用三层函数来实现。最外层函数接收装饰器参数,中间层函数接收原函数,最内层函数完成对原函数的调用以及附加功能。

代码示例:

import time

def deco(name="root"):
    # 最外层函数,接收装饰器参数
    def costTime(func):
        # 中间层,接收原函数
        def inner(*args, **kwargs):
            start = time.time()
            result = func(*args, **kwargs)
            end = time.time()
            print(f"执行{func.__name__}花了{end - start}s")
            print(f"传递的参数为:{name}")
            return result
        return inner
    return costTime

@deco(name="admin")
def add(a, b):
    time.sleep(1)
    return a + b

print("add(1, 2) =", add(1, 2))

1. 闭包与装饰器基本原理

  • 闭包概念

    • 闭包由外部函数和内部函数构成。内部函数可以访问外部函数的局部变量,即使外部函数已执行完毕。
  • 装饰器本质

    • 装饰器利用闭包特性,在不修改原函数代码的情况下,为其添加额外功能,如日志记录、性能计时、权限验证等。
    • 装饰器在定义时会先执行外层逻辑(例如打印提示),而返回的内部包装函数在调用时添加附加操作,再调用原函数。

2. 装饰器基础写法

  • 装饰器函数通常接收一个函数作为参数,并返回一个新的包装函数。
  • 包装函数内通常先执行预处理逻辑(如记录开始时间),再调用原函数,最后执行后续操作(如计算耗时、打印结果)。
  • 这种模式实现了对原函数功能的无侵入式扩展。

3. 保留原函数元数据

  • 使用装饰器后,包装函数会覆盖原函数的名称、文档字符串等元数据。
  • 为了保留这些信息,可以使用 functools.wraps 装饰包装函数,使得包装函数复制原函数的元数据。

4. 带参数的装饰器

  • 带参数的装饰器需要通过三层函数嵌套实现:
    • 最外层函数:接收装饰器参数,用于动态配置装饰行为。
    • 中间层函数:接收原函数,并返回包装函数。
    • 最内层函数:在调用原函数前后添加额外逻辑,同时传递所有参数。
  • 这种结构使装饰器可以灵活地根据传入参数调整附加功能。

5. 总结

  • 装饰器是 Python 中一种强大的功能扩展机制,基于高阶函数和闭包实现。
  • 常见应用场景包括性能统计、日志记录、缓存、权限控制等。
  • 设计装饰器时,注意保留原函数元数据(使用 functools.wraps)和正确处理参数传递问题。
  • 带参数的装饰器结构虽然稍复杂,但能够大幅提升装饰器的灵活性和可配置性。
    以下是关于使用类方法实现装饰器以及魔术方法__call__的学习笔记:

类实现装饰器

1. 使用类实现装饰器

在Python中,装饰器通常用于在不修改原函数代码的情况下,给函数增加额外的功能。除了使用函数实现装饰器外,还可以通过类来实现。这需要在类中实现__call__方法,使其实例化对象变为可调用对象,类似于函数。

示例:

import time

class DecoClass:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        start = time.time()
        result = self.func(*args, **kwargs)
        end = time.time()
        print(f"执行{self.func.__name__}花了{end - start}s")
        return result

@DecoClass  # 等同于:add = DecoClass(add)
def add(a, b):
    time.sleep(1)
    return a + b

print(add(1, 2))

说明:

  • DecoClass类的__init__方法接收一个函数作为参数,并将其赋值给实例变量self.func
  • __call__方法使得类的实例对象可以像函数一样被调用。在调用时,记录函数执行前后的时间,并计算执行时间。
  • 使用@DecoClass语法糖,将add函数装饰,使其在调用时自动计算并输出执行时间。

2. 带参数的装饰器类实现

有时,我们希望装饰器本身也能接受参数。这可以通过在类的__init__方法中接收装饰器参数,并在__call__方法中再定义并返回一个内部函数来实现。

示例:

import time

class DecoClass:
    def __init__(self, name):
        self.name = name

    def __call__(self, func):
        def inner(*args, **kwargs):
            start = time.time()
            result = func(*args, **kwargs)
            end = time.time()
            print(f"执行{func.__name__}花了{end - start}s")
            return result
        return inner

@DecoClass(name="admin")  # 等同于:add = DecoClass(name="admin")(add)
def add(a, b):
    time.sleep(1)
    return a + b

print(add(1, 2))

说明:

  • DecoClass类的__init__方法接收装饰器参数name,并将其赋值给实例变量self.name
  • __call__方法接收一个函数func作为参数,并定义一个内部函数inner,用于包装原函数的执行,并计算其执行时间。
  • 使用@DecoClass(name="admin")语法糖,将add函数装饰,使其在调用时自动计算并输出执行时间。

3. 魔术方法__call__

__call__是Python中的一种魔术方法,使得类的实例对象可以像函数一样被调用。通过在类中实现__call__方法,可以定义实例对象被调用时的行为。这在实现装饰器、回调函数等场景中非常有用。

示例:

class MyCallable:
    def __init__(self, value):
        self.value = value

    def __call__(self, increment):
        self.value += increment
        print(f"Value is now {self.value}")

obj = MyCallable(10)
obj(5)  # 输出:Value is now 15

说明:

  • MyCallable类实现了__call__方法,因此其实例对象obj可以像函数一样被调用。
  • 调用obj(5)时,实际执行的是obj.__call__(5),这使得self.value增加了increment的值。

相关文章:

  • 20届智能车赛规则已完成搜索
  • spring-security原理与应用系列:建造者
  • AI Agent开发大全第七课-个人如何申请到靠谱的AI
  • git日常学习
  • 五、重学C++—类(封装继承)
  • QT实现WPS功能
  • AI:如何用 MeloSpyGUI 和 MeloSpySuite 生成爵士音乐文件
  • 如何让自动驾驶汽车“看清”世界?坐标映射与数据融合概述
  • Java学习路线(便于理解)
  • “统计视角看世界”专栏阅读引导
  • 接上一主题,直接对二进制进行加密,密钥不写入电脑。
  • .NET 9 彻底改变了 API 文档:从 Swashbuckle(Swagger) 到 Scalar
  • AI比人脑更强,因为被植入思维模型【17】万物联系思维模型
  • 低功耗蓝牙(BLE)方案设计实战指南
  • 程序代码篇---SQLite数据库存储信息
  • 操作系统的特征
  • 程序设计语言的分类和特点
  • 学习本地部署DeepSeek的过程(基于ollama)
  • 产品经理如何管理需求池
  • Spring AOP 核心概念与实践指南
  • 新华时评:博物馆正以可亲可近替代“高冷范儿”
  • 尹锡悦宣布退出国民力量党
  • 夜读丨什么样的前程值得把春天错过
  • 马上评|文玩字画竞拍轻松赚差价?严防这类新型传销
  • 坚持吃素,是不是就不会得高血脂了?
  • 马上评|这种“维权”已经不算薅羊毛,涉嫌犯罪了