【单点知识】 Python装饰器介绍

文章目录

    • [0. 前言](#0. 前言)
    • [1. 为什么突然冒出"装饰器"?](#1. 为什么突然冒出“装饰器”?)
    • [2. 装饰器的基础:嵌套函数(函数里还能再"生"函数)](#2. 装饰器的基础:嵌套函数(函数里还能再“生”函数))
    • [3. 装饰器的常见用途](#3. 装饰器的常见用途)
    • [4. 常用装饰器代码实例](#4. 常用装饰器代码实例)
      • [4.1 最简骨架](#4.1 最简骨架)
      • [4.2 缓存装饰器(官方现成的)](#4.2 缓存装饰器(官方现成的))
      • [4.3 重试装饰器(带参数版)](#4.3 重试装饰器(带参数版))
    • [5. 小结](#5. 小结)

0. 前言

📣按照国际惯例,首先声明:本文只是我自己学习的理解,虽然参考了他人的宝贵见解及成果,但是内容可能存在不准确的地方。如果发现文中错误,希望批评指正,共同进步。

本文通过实例介绍Python中的装饰器。

1. 为什么突然冒出"装饰器"?

举个真场景: 产品经理说:"把这个Python脚本中的每个函数的运行时间打印出来。"

你最直观的写法:

python 复制代码
import time

def download():
    t0 = time.time()
    ...  # 真正的下载代码
    print(f"download 花了 {time.time()-t0:.2f} 秒")

def upload():
    t0 = time.time()
    ...  # 真正的上传代码
    print(f"upload 花了 {time.time()-t0:.2f} 秒")

问题立刻来了:

  1. 复制粘贴到吐,文件里全是 t0 = time.time()
  2. 哪天老板要改成写日志,你得全部再改一遍。
  3. 真正的业务(下载/上传)被淹没在一堆"边角料"里。
  4. 最最关键的是coding大佬根本不允许你动他的代码,说你写的都是屎山!

装饰器就是来解决这种"想给函数加点通用小功能,又不想改它本身"的痛点。

2. 装饰器的基础:嵌套函数(函数里还能再"生"函数)

Python 里函数像字符串、数字一样,可以当变量传来传去

嵌套函数 = 在函数 内部def 一个函数,通常用于:

  1. 隐藏实现细节
  2. 做闭包(记住外层变量的值)
  3. 给装饰器当"脚手架"

看个简单例子:

python 复制代码
def make_adder(base):      # 外层
    def adder(x):          # 内层
        return base + x    # 内层记住了 base
    return adder           # 把内层函数当"商品"返回

plus5 = make_adder(5)      # 现在 plus5 是一个"加 5"机器
print(plus5(7))            # 12

plus5 手里一直攥着 base=5,这就是闭包

装饰器全靠这个本事:外层把原函数"记住",内层在前后加点料,再返回内层函数即可。

3. 装饰器的常见用途

装饰器是一种语法糖,他的作用就是:"不碰原函数一行代码,就能给它前后塞点额外操作。" 它的常见用途有以下几类:

  • 打印日志 / 计时
  • 缓存结果(斐波那契算过一次就记住)
  • 重试网络请求
  • 检查用户权限
  • 路由注册(Flask、FastAPI)

语法糖的介绍请见:【单点知识】Python语法糖------让代码更甜的方法

4. 常用装饰器代码实例

4.1 最简骨架

python 复制代码
import time, functools

def timer(func):                # 外层:收到原函数
    @functools.wraps(func)      # 保留原函数名字&文档
    def wrapper(*args, **kw):   # 内层:真正会被调用的新函数
        t0 = time.time()
        result = func(*args, **kw)   # 调原函数
        print(f"{func.__name__} 花了 {time.time()-t0:.3f} 秒")
        return result
    return wrapper              # 把新函数返还给外界

使用:贴贴纸即可,例如

python 复制代码
import time, functools
import numpy as np

def timer(func):                # 外层:收到原函数
    @functools.wraps(func)      # 保留原函数名字&文档
    def wrapper(*args, **kw):   # 内层:真正会被调用的新函数
        t0 = time.time()
        result = func(*args, **kw)   # 调原函数
        print(f"{func.__name__} 花了 {time.time()-t0:.3f} 秒")
        return result
    return wrapper              # 把新函数返还给外界

@timer
def mm(x,y): return np.matmul(x,y)    

if __name__ =='__main__':
    x = np.random.rand(9999,9999)
    y = np.random.rand(9999,9999)
    mm(x, y)

输出为:

python 复制代码
mm 花了 4.049 秒

4.2 缓存装饰器(官方现成的)

@lru_cache 把函数每次的计算结果自动存进一张"备忘录",下次撞见相同输入直接拿答案,绝不再算第二次。 加一行就能让递归、重复查询等代码从"龟速"变"秒回"。

python 复制代码
from functools import lru_cache
import time

@lru_cache(maxsize=None)
def fib(n):
    return n if n < 2 else fib(n-1) + fib(n-2)

t0 = time.time()
fib(999)          
print(f"耗时:{time.time() - t0}:.10" )

t0 = time.time()
fib(1000)        
print(f"耗时:{time.time() - t0}:.10")

输出为:

python 复制代码
耗时:0.0010073184967041016:.10
耗时:0.0:.10

第一次fib(999):把 fib(0)...fib(999) 共 1000 个值全部算出并缓存。

第二次fib(1000) :只需算 1 个新值fib(1000) = fib(999) + fib(998),fib(999) 和fib(998)这两个直接从缓存拿。

4.3 重试装饰器(带参数版)

重装饰器就是先收参数再返回真正的装饰器,所以一眼看过去就是 @xxx(arg) 的双层结构。

python 复制代码
def retry(times=3, wait=1):
    def decorator(func):
        @functools.wraps(func)  #这不是重装饰器
        def wrapper(*args, **kw):
            for i in range(1, times+1):
                try:
                    return func(*args, **kw)
                except Exception as e:
                    print(f"第{i}次失败:{e}")
                    if i == times:
                        raise
                    time.sleep(wait)
        return wrapper
    return decorator

@retry(times=5)
def connect():
    ...

@functools.wraps(func) 虽然"带括号",但它只是一次性返回真正装饰器的辅助工具,本身并不接受"用户自定义"参数,习惯上仍归为普通装饰器,而不是"带参装饰器(重装饰器)"。

5. 小结

  1. 嵌套函数 = 函数里 def 函数 + 闭包记忆
  2. 装饰器 = 嵌套函数 + 返回新函数 + @ 语法糖
  3. 口诀:"原函数不动,前后功能任意缝;撕掉装饰器,业务照样跑。"

记住:当你出现"又要给所有函数加同一件小事"的冲动时,先想装饰器------

写一次,贴万张,代码干净不慌张。

相关推荐
带土12 小时前
2. C++ private、protected、public
开发语言·c++
Jackson@ML2 小时前
2026最新版Sublime Text 4安装使用指南
java·python·编辑器·sublime text
我不是8神2 小时前
字节跳动 Eino 框架(Golang+AI)知识点全面总结
开发语言·人工智能·golang
TonyLee0172 小时前
半监督学习介绍
人工智能·python·深度学习·机器学习
古城小栈2 小时前
Rust复合类型 四大军阀:数、元、切、串
开发语言·后端·rust
kong79069283 小时前
Python核心语法-Python自定义模块、Python包
开发语言·python·python核心语法
数智工坊3 小时前
【操作系统-文件管理】
数据结构·数据库
OLOLOadsd1233 小时前
基于Mask-RCNN和RegNetX的茎蛀虫检测识别系统详解
python
oioihoii3 小时前
Oracle迁移KingbaseES实战
数据库·oracle