【大模型2609·Python基础·12】高阶编程艺术:装饰器深度剖析与现代泛型系统

专栏导读:掌握 Python 最优雅的元编程范式与现代工程类型契约。本篇系统推导装饰器的诞生过程,剖析多层装饰器执行顺序、带参装饰器与类装饰器的设计模式;并解读现代 Python 的类型提示(Type Hints)与泛型(Generic)支持,助力构建严谨的工业级代码。


12.6 装饰器

12.6.1 什么是装饰器

装饰器允许在不修改原有函数代码的基础上,动态地增加或修改函数的功能。装饰器本质上是一个接收函数作为输入并返回一个新的包装过后的函数的对象。

语法格式:

1
2
3
4
5
6
7
def decorator(func):
def inner(参数):
# 添加功能
func(参数)
# 添加功能

return inner

decorator 是一个装饰器函数,它接受一个函数 func 作为参数,并返回一个内部函数 inner。在 inner 函数内部,我们可以执行一些额外的操作,然后调用原始函数 func,并返回其结果。

12.6.2 定义和使用装饰器

当我们定义完一个装饰器函数之后,有2种方式来用它装饰其他函数:

  • 直接调用装饰器函数,并将需要装饰的函数作为参数传给装饰器函数,并用变量接收结果。
    • 如果变量与被装饰的函数同名,那么被装饰的函数功能已经发生了改变,它的功能 = 原来的功能 + 装饰器增加的功能
    • 如果变量与被装饰的函数不同名,那么就相当于定义了一个新函数,它的功能 = 被装饰函数的功能 + 装饰器增加的功能,而原来的被装饰函数功能不变
  • 使用@装饰器函数名标记需要被装饰的函数。此时被装饰函数的功能就 = 原来的功能 + 装饰器增加的功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
"""
演示装饰器
"""
from math import sqrt

# 定义一个装饰器函数
def get_absolute(f): # 外部函数
# 定义内部函数
def inner(x):
x = abs(x) # 求x的绝对值
return f(x) # 内部函数使用了外部函数的参数f,所以会创建闭包

return inner # 返回内部函数

# (1)方式一:调用get_absolute装饰器函数装饰fun_one函数
# 定义一个函数
def fun_one(x):
return sqrt(x)
fun_one = get_absolute(fun_one) # fun_one已经被装饰器get_absolute装饰过了
print(fun_one(-9)) # 3.0

# (2)方式二:使用@的语法,使用get_absolute装饰器函数装饰fun_two函数
# 定义一个函数
@get_absolute
def fun_two(x):
return sqrt(x)
print(fun_two(-9)) # 3.0

12.6.3 多层装饰器

多个装饰器的装饰过程:离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
"""
演示多层装饰器
"""
from math import sqrt

# 定义一个装饰器函数
def get_absolute(f): # 外部函数
# 定义内部函数
def inner(x):
x = abs(x) # 求x的绝对值
return f(x) # 内部函数使用了外部函数的参数f,所以会创建闭包

return inner # 返回内部函数

# 定义另一个装饰器函数
def get_integer(f):
def inner(x):
x = int(x)
return f(x)

return inner

@get_integer
@get_absolute
def get_sqrt(x):
"""开根号"""
return sqrt(x)
# 使用多个装饰器装饰get_sqrt函数

print(get_sqrt("-4")) # 2.0

12.6.4 带参数的装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
"""
演示带参数的装饰器
"""
from math import sqrt

# 求根号n次
def times(n):
# 将参数转换为非负数
def get_absolute(f):
def inner(x):
x = abs(x)
for i in range(n):
x = f(x)
return x

return inner
return get_absolute

# 使用times装饰器函数装饰get_sqrt函数
@times(3)
def get_sqrt(x):
"""开根号"""
return sqrt(x)

#调用get_sqrt函数
print(get_sqrt(-256))

12.6.5 类装饰器

类装饰器是包含 __call__() 方法的类,它接受函数作为参数,并返回新的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
"""
演示类装饰器
"""
from math import sqrt

# 定义一个类,它是一个装饰器类
class AbsoluteDecoratorClass:
def __init__(self, f):
self.f = f

def __call__(self, x):
x = abs(x)
return self.f(x)

# 使用AbsoluteDecoratorClass类装饰get_sqrt函数
@AbsoluteDecoratorClass
def get_sqrt(x):
"""开根号"""
return sqrt(x)

# 调用get_sqrt函数
print(get_sqrt(-4)) # 2.0

12.7 泛型(了解)

Python 有泛型,但核心作用是类型提示(而非运行时类型约束),依赖typing模块(3.9 + 可简化)。Python 泛型不改变动态类型的本质,运行时仍可传入任意类型,仅辅助编码阶段的类型检查。

  • 类型提示(Type Hints):帮助开发者 / 编辑器(如 PyCharm)在编码阶段明确容器 / 函数的预期类型,提升代码可读性和维护性。
  • 静态类型检查工具:如mypy可以基于泛型的类型提示做静态检查,提前发现类型错误。

Python 3.5 + 引入typing模块(Python 3.9 + 可直接用内置类型),提供泛型相关的工具,核心包括:

  • 泛型容器:
    • Python 3.9之前:List[T]、Dict[K, V]、Tuple[T1, T2]、Set[T]等
    • Python 3.9之后:list[T]、dict[K, V]、tuple[T1, T2]、set[T]等
  • 自定义泛型类 / 函数:Generic[T]
  • 通用类型:Any、Union、Optional、Literal等
    • Any:表示变量/返回值可以是任意类型
    • Union[Type1, Type2, ...]表示变量 / 返回值可以是指定类型中的任意一种(Python 3.10 + 支持更简洁的Type1 | Type2写法)
    • Optional[T]是Union[T, None]的简写形式,表示变量 / 返回值可以是指定类型,也可以是None
    • Literal[字面量值1,字面值2,...],表示变量/返回值类型的值是指定几个字面量值,例如Literal["red", "blue", "green"]

12.7.1 mypy工具

第一步:安装mypy包

image-20260207233213898

第二步:添加mypy工具

字段 值
Name mypy
Program $PyInterpreterDirectory$/python
Arguments -m mypy $FilePath$或-m mypy --show-error-codes --strict $FilePath$
Working directory $ProjectFileDir$

image-20260207233320958

第三步:使用mypy工具

image-20260207233535561

image-20260207233638519

12.7.1 案例1:基础泛型类型提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. 泛型列表:明确列表元素是int类型
def sum_numbers(numbers: list[int]) -> int:
return sum(numbers)

# 2. 泛型字典:键是str,值是float
def get_scores() -> dict[str, float]:
return {"math": 95.5, "english": 88.0}

# 3. 泛型元组:固定长度和类型
def get_point() -> tuple[int, int]:
return (10, 20)

# 测试
print(sum_numbers([1, 2, 3])) # 输出:6
print(sum_numbers(["hello","world","python"]))
print(get_scores()) # 输出:{'math': 95.5, 'english': 88.0}
print(get_point()) # 输出:(10, 20)

12.7.2 案例2:自定义泛型类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from typing import Generic, TypeVar, List

T = TypeVar('T')

class Stack(Generic[T]):
"""泛型栈类"""
def __init__(self) -> None:
self.items: List[T] = []

def push(self, item: T) -> None:
self.items.append(item)

def pop(self) -> T:
return self.items.pop()

def is_empty(self) -> bool:
return len(self.items) == 0


# 使用示例
int_stack = Stack[int]() # 指定类型参数
int_stack.push(42)
value = int_stack.pop() # value: int

str_stack = Stack[str]()
str_stack.push("hello")
text = str_stack.pop() # text: str

12.7.3 案例3:泛型函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from typing import TypeVar, List

# 定义可接受任意类型的类型变量
T = TypeVar('T')

# 泛型函数:返回列表中第一个元素
def first_element(lst: List[T]) -> T:
if not lst:
raise ValueError("列表不能为空")
return lst[0]

# 测试不同类型列表
print(first_element([1, 2, 3])) # 输出:1(int类型)
print(first_element(["a", "b"])) # 输出:a(str类型)
print(first_element([3.14, 2.71])) # 输出:3.14(float类型)

12.7.4 案例4:字面量类型

1
2
3
4
5
6
7
from typing import Literal

def set_color(color: Literal["red", "blue", "green"]) -> None:
print(f"设置颜色为:{color}")

set_color("red") # 正常
set_color("yellow") # 编辑器/mypy报错:Argument of type "Literal['yellow']" cannot be assigned to parameter of type "Literal['red', 'blue', 'green']"

12.7.5 案例5:类型变量约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from typing import TypeVar, List

class Animal:
def speak(self) -> str:
return "..."

class Dog(Animal):
def speak(self) -> str:
return "Woof!"

class Cat(Animal):
def speak(self) -> str:
return "Meow!"

# 绑定到 Animal 或其子类
A = TypeVar('A', bound=Animal)

def make_sounds(animals: List[A]) -> List[str]:
return [animal.speak() for animal in animals]

dogs = [Dog(), Dog()]
cats = [Cat(), Cat()]
bears = ["熊大","熊二"]

dog_sounds = make_sounds(dogs) # ["Woof!", "Woof!"]
cat_sounds = make_sounds(cats) # ["Meow!", "Meow!"]
bear_sounds = make_sounds(bears)