Skip to content

泛型类型 ​

泛型让类型定义更灵活、可复用。例如 list[int] 和 list[str] 都是 list 的泛型实例,只是元素类型不同。

TypeVar:类型变量 ​

TypeVar 定义一个类型参数,在调用时绑定到具体类型:

python
from typing import TypeVar

T = TypeVar('T')

def first(items: list[T]) -> T:
    return items[0]

# T 被推断为 int
number = first([1, 2, 3])  # number: int

# T 被推断为 str
name = first(["Alice", "Bob"])  # name: str

不使用泛型的问题:

python
def first(items: list) -> object:  # 返回 object,丢失类型信息
    return items[0]

number = first([1, 2, 3])
print(number + 10)  # 类型检查器警告:object 不支持 +

泛型函数 ​

多个类型参数 ​

python
from typing import TypeVar

K = TypeVar('K')
V = TypeVar('V')

def make_pair(key: K, value: V) -> tuple[K, V]:
    return (key, value)

pair1 = make_pair("name", "Alice")  # tuple[str, str]
pair2 = make_pair(1, "Alice")       # tuple[int, str]

类型约束 ​

限制 TypeVar 只能是特定类型之一:

python
from typing import TypeVar

# 只能是 int 或 float
Number = TypeVar('Number', int, float)

def double(value: Number) -> Number:
    return value * 2

print(double(5))      # OK: int
print(double(3.14))   # OK: float
print(double("hi"))   # 类型错误

类型边界(Bound) ​

约束类型必须是某个类的子类:

python
from typing import TypeVar

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

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

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

T = TypeVar('T', bound=Animal)

def get_sound(animal: T) -> str:
    return animal.make_sound()  # 类型检查器知道 T 有 make_sound

dog = Dog()
print(get_sound(dog))  # OK

# get_sound("hello")  # 错误:str 不是 Animal 的子类

泛型类 ​

定义泛型类 ​

python
from typing import Generic, TypeVar

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] = Stack()
int_stack.push(1)
int_stack.push(2)
print(int_stack.pop())  # 2

str_stack: Stack[str] = Stack()
str_stack.push("hello")
# str_stack.push(42)  # 类型错误

多个类型参数 ​

python
from typing import Generic, TypeVar

K = TypeVar('K')
V = TypeVar('V')

class Pair(Generic[K, V]):
    def __init__(self, key: K, value: V) -> None:
        self.key = key
        self.value = value
    
    def get_key(self) -> K:
        return self.key
    
    def get_value(self) -> V:
        return self.value

pair = Pair("name", 42)  # Pair[str, int]
print(pair.get_key())    # str
print(pair.get_value())  # int

协变与逆变 ​

不变(Invariant) ​

默认情况下,泛型类型是不变的:

python
class Animal:
    pass

class Dog(Animal):
    pass

# list 是不变的
def feed_animals(animals: list[Animal]) -> None:
    pass

dogs: list[Dog] = [Dog()]
# feed_animals(dogs)  # 类型错误!list[Dog] 不是 list[Animal]

原因:如果允许传入 list[Dog],函数内部可能添加 Cat,破坏类型安全:

python
def feed_animals(animals: list[Animal]) -> None:
    animals.append(Cat())  # 如果 animals 是 list[Dog],这会出错

协变(Covariant) ​

T 是协变的:如果 Dog 是 Animal 的子类,则 Container[Dog] 是 Container[Animal] 的子类。

只读容器可以协变:

python
from typing import TypeVar

T_co = TypeVar('T_co', covariant=True)

class ReadOnlyBox(Generic[T_co]):
    def __init__(self, value: T_co) -> None:
        self._value = value
    
    def get(self) -> T_co:
        return self._value

def process_animal(box: ReadOnlyBox[Animal]) -> None:
    print(box.get())

dog_box: ReadOnlyBox[Dog] = ReadOnlyBox(Dog())
process_animal(dog_box)  # OK,因为 ReadOnlyBox 是协变的

逆变(Contravariant) ​

T 是逆变的:如果 Dog 是 Animal 的子类,则 Container[Animal] 是 Container[Dog] 的子类。

只写容器可以逆变:

python
from typing import TypeVar

T_contra = TypeVar('T_contra', contravariant=True)

class Feeder(Generic[T_contra]):
    def feed(self, animal: T_contra) -> None:
        print(f"Feeding {animal}")

def train_dog(feeder: Feeder[Dog]) -> None:
    feeder.feed(Dog())

animal_feeder: Feeder[Animal] = Feeder()
train_dog(animal_feeder)  # OK,因为 Feeder 是逆变的

规则总结:

  • 不变(默认):可读可写
  • 协变(covariant=True):只读(输出位置)
  • 逆变(contravariant=True):只写(输入位置)

实战案例:泛型容器 ​

python
from typing import Generic, TypeVar, Optional

T = TypeVar('T')

class LinkedListNode(Generic[T]):
    def __init__(self, value: T) -> None:
        self.value = value
        self.next: Optional[LinkedListNode[T]] = None

class LinkedList(Generic[T]):
    def __init__(self) -> None:
        self.head: Optional[LinkedListNode[T]] = None
    
    def append(self, value: T) -> None:
        new_node = LinkedListNode(value)
        if self.head is None:
            self.head = new_node
        else:
            current = self.head
            while current.next is not None:
                current = current.next
            current.next = new_node
    
    def to_list(self) -> list[T]:
        result: list[T] = []
        current = self.head
        while current is not None:
            result.append(current.value)
            current = current.next
        return result

# 使用
numbers = LinkedList[int]()
numbers.append(1)
numbers.append(2)
numbers.append(3)
print(numbers.to_list())  # [1, 2, 3]

names = LinkedList[str]()
names.append("Alice")
names.append("Bob")
print(names.to_list())  # ['Alice', 'Bob']

泛型装饰器 ​

python
from typing import TypeVar, Callable
import time

T = TypeVar('T')

def timer(func: Callable[..., T]) -> Callable[..., T]:
    def wrapper(*args, **kwargs) -> T:
        start = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - start
        print(f"{func.__name__} took {elapsed:.4f}s")
        return result
    return wrapper

@timer
def slow_function(n: int) -> int:
    time.sleep(1)
    return n * 2

result = slow_function(5)  # result: int

小结 ​

  • TypeVar 定义类型变量,实现泛型
  • Generic[T] 创建泛型类
  • Bound 约束类型必须是某个类的子类
  • 协变(covariant)用于只读容器
  • 逆变(contravariant)用于只写容器
  • 默认是不变(invariant)

下一节学习 Protocol,它提供结构化子类型。


参考:PEP 484 – Type Hints

练习:答案

用心记录每一步成长