简单入门Python装饰器

引言

在Python中,装饰器(Decorator)是一种强大的工具,它使用简单的@符号语法,却能实现令人惊叹的代码增强功能。

装饰器初体验

1.1 最简单的装饰器示例

python 复制代码
def simple_decorator(func):
    def wrapper():
        print("函数执行前...")
        func()
        print("函数执行后...")
    return wrapper

@simple_decorator
def say_hello():
    print("Hello!")

say_hello()
"""
输出:
函数执行前...
Hello!
函数执行后...
"""

1.2 装饰器的本质

装饰器本质上是一个高阶函数,它:

  1. 接受一个函数作为参数
  2. 返回一个新函数
  3. 通常在不修改原函数代码的情况下增强其功能

@decorator 只是语法糖,等价于:

python 复制代码
def say_hello(): ...
say_hello = decorator(say_hello)

为什么需要装饰器?

2.1 代码复用:DRY原则

避免重复代码(Don't Repeat Yourself):

python 复制代码
import time

# 没有装饰器的重复代码
def funco1():
    start = time.time()
    # 函数逻辑...
    time.sleep(2)
    end = time.time()
    print(f"函数 {funco1.__name__} 耗时: {end-start}秒")

def funco2():
    start = time.time()
    # 函数逻辑...
    time.sleep(3)
    end = time.time()
    print(f"函数 {funco2.__name__} 耗时: {end-start}秒")

def timing(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"函数 {func.__name__} 耗时: {end-start}秒")
        return result
    return wrapper

@timing
def func1():
    # 函数逻辑...
    time.sleep(2)
    print('func1 ....')

@timing
def func2():
    time.sleep(3)
    # 函数逻辑...
    print('func2 ....')

if __name__ == '__main__':
    funco1()
    funco2()
    func1()
    func2()

2.2 分离关注点

将业务逻辑与横切关注点(如日志、权限检查)分离:

python 复制代码
@login_required
@log_execution
def delete_user(user_id):
    # 纯业务逻辑
    ...

装饰器进阶用法

3.1 带参数的装饰器

python 复制代码
def repeat(num_times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(num_times=3)
def greet(name):
    print(f"Hello {name}")

greet("Alice")
"""
Hello Alice
Hello Alice
Hello Alice
"""

3.2 保留原函数元信息

使用functools.wraps保持原函数的__name__等属性:

python 复制代码
from functools import wraps

def logged(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"调用 {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

3.3 类装饰器

python 复制代码
class CountCalls:
    def __init__(self, func):
        self.func = func
        self.num_calls = 0
    
    def __call__(self, *args, **kwargs):
        self.num_calls += 1
        print(f"调用次数: {self.num_calls}")
        return self.func(*args, **kwargs)

@CountCalls
def say_hello():
    print("Hello!")

say_hello()  # 输出调用次数和Hello!
say_hello()  # 输出调用次数和Hello!

"""
调用次数: 1
Hello!
调用次数: 2
Hello!
"""

结语

点个赞,关注我获取更多实用 Python 技术干货!如果觉得有用,记得收藏本文!

相关推荐
Scott9999HH1 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
不好听6132 小时前
从一行 JSX 到屏幕像素:前端开发者必须懂的浏览器渲染管线
前端
恒拓高科WorkPlus2 小时前
企业级内网即时通讯建设模型:BeeWorks内网IM四层架构
前端
AI云海3 小时前
python 列表、元组、集合和字典
开发语言·python
前端小李子3 小时前
前端环境变量裸奔?我用 EnvShield 给它穿了件防弹衣
前端
youtootech3 小时前
HarmonyOS《柚兔学伴》项目实战25-我的页面、Web 嵌入与项目总结
前端·华为·harmonyos
二十雨辰3 小时前
[爬虫]-Urllib
爬虫·python
小林ixn3 小时前
从零到一理解 React 父子组件通信:手写一个 Todo 应用带你彻底搞懂单向数据流
前端·javascript·react.js
醇氧4 小时前
CountDownLatch / CyclicBarrier / Semaphore 面试高频问答清单
前端·面试·职场和发展