专栏导读 :掌握 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 sqrtdef get_absolute (f ): def inner (x ): x = abs (x) return f(x) return inner def fun_one (x ): return sqrt(x) fun_one = get_absolute(fun_one) print (fun_one(-9 )) @get_absolute def fun_two (x ): return sqrt(x) print (fun_two(-9 ))
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 sqrtdef get_absolute (f ): def inner (x ): x = abs (x) return f(x) 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) print (get_sqrt("-4" ))
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 sqrtdef 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(3 ) def get_sqrt (x ): """开根号""" return sqrt(x) 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 sqrtclass AbsoluteDecoratorClass : def __init__ (self, f ): self.f = f def __call__ (self, x ): x = abs (x) return self.f(x) @AbsoluteDecoratorClass def get_sqrt (x ): """开根号""" return sqrt(x) print (get_sqrt(-4 ))
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包
第二步:添加mypy工具
字段
值
Name
mypy
Program
$PyInterpreterDirectory$/python
Arguments
-m mypy $FilePath$或-m mypy --show-error-codes --strict $FilePath$
Working directory
$ProjectFileDir$
第三步:使用mypy工具
12.7.1 案例1:基础泛型类型提示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def sum_numbers (numbers: list [int ] ) -> int : return sum (numbers) def get_scores () -> dict [str , float ]: return {"math" : 95.5 , "english" : 88.0 } def get_point () -> tuple [int , int ]: return (10 , 20 ) print (sum_numbers([1 , 2 , 3 ])) print (sum_numbers(["hello" ,"world" ,"python" ]))print (get_scores()) print (get_point())
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() str_stack = Stack[str ]() str_stack.push("hello" ) text = str_stack.pop()
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 ])) print (first_element(["a" , "b" ])) print (first_element([3.14 , 2.71 ]))
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" )
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!" 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) cat_sounds = make_sounds(cats) bear_sounds = make_sounds(bears)