[Python] Python的 类型系统 : `type` 函数 和 `typing` 模块

1 概述:Python type 函数

1.1 首先澄清一个概念

严格来说,type 在 Python 中是一个内置函数,而不是一个独立的模块。它有两个主要用途:

  1. 查看对象的类型(最常用的用法)
  2. 动态创建类(高级用法)

1.2 基础用法:查看类型

# 基本用法
print(type(123))       # <class 'int'>
print(type("hello"))   # <class 'str'>
print(type([1, 2, 3])) # <class 'list'>
print(type({"a": 1}))  # <class 'dict'>

为什么用 type() 而不是 isinstance()

# type() 返回的是精确类型
print(type([]) == list)     # True
print(type([]) == object)   # False(虽然 list 继承自 object)

# isinstance() 会考虑继承关系
print(isinstance([], list))   # True
print(isinstance([], object)) # True

经验法则:判断具体类型用 type(),判断"是不是某种类型"用 isinstance()


1.3 高级用法:动态创建类

这是 type() 的"隐藏功能",也是理解 Python 元类(metaclass)的关键。

1.3.1 类创建的底层原理

# 你平时写的类
class Person:
  name = "unknown"
  
  def say_hello(self):
      return f"Hello, I'm {self.name}"

# 等价于用 type() 动态创建
Person = type('Person', # 类名
            (object,), # 父类元组
            {          # 属性字典
                'name': 'unknown',
                'say_hello': lambda self: f"Hello, I'm {self.name}"
            })

1.3.2 type() 创建类的语法

type(name, bases, namespace)
# name: 类名字符串
# bases: 父类元组(可以为空)
# namespace: 包含属性和方法的字典

1.3.3 实际应用示例

def create_class(class_name, fields):
  """
  动态创建数据类(类似简单的 dataclass)
  """
  def __init__(self, **kwargs):
      for field in fields:
          setattr(self, field, kwargs.get(field))
  
  def __repr__(self):
      attrs = ", ".join(f"{f}={getattr(self, f)!r}" for f in fields)
      return f"{class_name}({attrs})"
  
  # 用 type 动态创建类
  return type(class_name, (), {
      '__init__': __init__,
      '__repr__': __repr__,
      '__slots__': fields # 限制属性,节省内存
  })

# 使用
Student = create_class('Student', ['name', 'age', 'major'])
s = Student(name="张三", age=20, major="CS")
print(s) # Student(name='张三', age=20, major='CS')

1.4 type 与元类(Metaclass)

1.4.1 一切都是对象,类也是

# 在 Python 中,类也是对象!
class Foo:
  pass

print(type(Foo)) # <class 'type'>

# 这意味着:
# 1. Foo 是 type 的实例
# 2. type 是 Foo 的"类"

1.4.2 元类:类的类

# type 是所有类的默认元类
print(type(int))   # <class 'type'>
  # 补充: print(type(123))   # <class 'int'>
print(type(str))   # <class 'type'>
  # 补充: print(type("123"))   # <class 'str'>
print(type(list))  # <class 'type'>
  # 补充: print(type([1,2,3]))   # <class 'list'>
  # 补充: print(type({"a": 1}))   # <class 'dict'>
print(type(type))  # <class 'type'> (type 也是自己的实例!)

1.4.3 自定义元类

class SingletonMeta(type):
  """单例元类:确保类只有一个实例"""
  _instances = {}
  
  def __call__(cls, *args, **kwargs):
      if cls not in cls._instances:
          cls._instances[cls] = super().__call__(*args, **kwargs)
      return cls._instances[cls]

class Database(metaclass=SingletonMeta):
  def __init__(self, url):
      self.url = url

# 测试
db1 = Database("mysql://localhost")
db2 = Database("postgres://remote")
print(db1 is db2)     # True(同一个对象)
print(db1.url)        # mysql://localhost(第一个创建的)

1.5 实际应用场景

场景 用法
调试/日志 print(type(x)) 快速查看变量类型
类型检查 if type(x) is not int: raise TypeError
序列化/反序列化 根据类型名动态创建类
ORM 框架 动态生成模型类
插件系统 运行时动态注册和创建类
单例/工厂模式 用元类控制类的创建行为

1.6 总结要点

  1. type(obj) → 返回对象的类型(最常用)
  2. type(name, bases, dict) → 动态创建类
  3. 所有类都是 type 的实例type 是默认元类
  4. 自定义元类 → 控制类的创建过程(高级)

1.7 思考题(供练习)

# 这段代码输出什么?为什么?
class A: pass
class B(A): pass
print(type(A))
print(type(B))
print(type(A) == type(B))
print(A.__class__)
print(B.__class__.__class__)
点击查看答案
<class 'type'>
<class 'type'>
True
<class 'type'>
<class 'type'>

所有类的 __class__ 都是 type(默认元类),所以 type(A) == type(B)True


type 是理解 Python 动态特性的关键,掌握它对后续学习元编程、框架设计都很有价值。

2 概述:Python typing 模块 ( Python 类型提示系统 )

Python 的 typing 模块,这是 Python 类型提示系统的核心,对于写清晰、可维护的代码非常重要。

2.1 为什么需要 typing?

背景:Python 是动态类型语言

# 动态类型的"自由"也带来了问题
def add(a, b):
  return a + b

add(1, 2)       # 3 ✓
add("1", "2")   # "12" ✓(可能不是想要的)
add([1], [2])   # [1, 2] ✓(也可能不是想要的)

问题:函数签名没有表达设计意图,调用者容易用错。

解决方案:类型提示(Type Hints)

from typing import List, Dict, Optional

def add(a: int, b: int) -> int:
  return a + b

# 现在 IDE 会提示错误,代码也更易读
add("1", "2") # IDE 警告:期望 int,实际为 str

注意:Python 解释器不强制类型检查,类型提示是给开发者、IDE 和类型检查工具(如 mypy)看的。

1.2 typing 模块的核心内容

2.1 基础类型别名

from typing import List, Dict, Tuple, Set, Optional, Union, Callable

# 容器类型(Python 3.9+ 可直接用 list, dict 等,不需要导入)
numbers: List[int] = [1, 2, 3]
scores: Dict[str, int] = {"Alice": 90, "Bob": 85}
point: Tuple[int, int] = (10, 20)         # 固定长度元组
record: Tuple[int, str, float] = (1, "a", 3.14) # 异构元组
tags: Set[str] = {"python", "typing"}

2.2 Optional:可能为 None

from typing import Optional

# 老写法(繁琐)
def find_user(user_id: int) -> Union[User, None]:
  ...

# 推荐写法:Optional[X] 等价于 Union[X, None]
def find_user(user_id: int) -> Optional[User]:
  """返回 User 或 None(找不到时)"""
  ...

2.3 Union:多种可能类型

from typing import Union

# 参数可以是 int 或 float
def process(value: Union[int, float]) -> float:
  return float(value)

# Python 3.10+ 新语法(更简洁)
def process(value: int | float) -> float: # 用 | 代替 Union
  return float(value)

1.3 复杂类型场景

1.3.1 Callable:函数类型

from typing import Callable

# 定义回调函数类型
# Callable[[参数类型...], 返回类型]
def execute_callback(
  callback: Callable[[int, int], int],
  x: int,
  y: int
) -> int:
  return callback(x, y)

# 使用
def add(a: int, b: int) -> int:
  return a + b

result = execute_callback(add, 1, 2) # 3

1.3.2 泛型:类型参数化

from typing import TypeVar, Generic, 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()

# 使用
int_stack: Stack[int] = Stack()
int_stack.push(1)     # ✓
int_stack.push("a")   # IDE 警告:期望 int

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

1.3.3 有界类型变量

from typing import TypeVar

# T 必须是 Number 或其子类
class Number:
  def value(self) -> float: ...

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

def sum_numbers(items: List[T]) -> float:
  return sum(item.value() for item in items)

1.4 高级特性

1.4.1 TypeAlias:类型别名(Python 3.10+)

from typing import TypeAlias

# 简化复杂类型
Vector: TypeAlias = list[float]
Matrix: TypeAlias = list[Vector]

def dot_product(v1: Vector, v2: Vector) -> float:
  return sum(x * y for x, y in zip(v1, v2))

# 更清晰的函数签名
def matrix_multiply(a: Matrix, b: Matrix) -> Matrix:
  ...

1.4.2 Protocol:结构子类型(鸭子类型)

from typing import Protocol

class Drawable(Protocol):
  """只要实现了 draw() 方法,就符合这个协议"""
  def draw(self) -> None: ...

def render(items: list[Drawable]) -> None:
  for item in items:
      item.draw()

# 不需要【显式继承】!
class Circle:
  def draw(self) -> None:
      print("Drawing circle")

class Square:
  def draw(self) -> None:
      print("Drawing square")

# Circle 和 Square 都"符合" Drawable 协议
render([Circle(), Square()]) # ✓

1.4.3 @overload:函数重载

from typing import overload, List

class Processor:
  @overload
  def process(self, data: int) -> str: ...
  
  @overload
  def process(self, data: List[int]) -> List[str]: ...
  
  # 实际实现
  def process(self, data):
      if isinstance(data, int):
          return str(data)
      return [str(x) for x in data]

p = Processor()
p.process(42)       # IDE 知道返回 str
p.process([1, 2, 3]) # IDE 知道返回 List[str]

1.5 实际项目应用

1.5.1 数据模型定义(配合 dataclasses)

from dataclasses import dataclass
from typing import Optional, List
from datetime import datetime

@dataclass
class User:
  id: int
  name: str
  email: Optional[str] = None
  created_at: datetime = field(default_factory=datetime.now)

@dataclass 
class Post:
  id: int
  title: str
  content: str
  author: User # 引用其他类型
  tags: List[str] = field(default_factory=list)

15.2 API 响应类型

from typing import TypedDict

class ApiResponse(TypedDict):
  code: int
  message: str
  data: dict

class UserInfo(TypedDict):
  id: int
  name: str
  is_active: bool

# 函数明确返回结构
def get_user(user_id: int) -> ApiResponse:
  return {
      "code": 200,
      "message": "success",
      "data": {"id": user_id, "name": "Alice", "is_active": True}
  }

1.6 版本演进与最佳实践

Python 版本 重要变化
3.5 typing 模块引入
3.7 from __future__ import annotations 延迟求值
3.9 内置类型支持泛型:list[int] 替代 List[int]
3.10 X | Y 替代 Union[X, Y]TypeAlias
3.11 Self 类型(类方法返回自身类型)

现代推荐写法(Python 3.9+)

# 不再需要 from typing import List, Dict, Optional, Union

def process(
  items: list[int | str],          # 替代 List[Union[int, str]]
  config: dict[str, float] | None  # 替代 Optional[Dict[str, float]]
) -> tuple[int, str]:
  ...

1.7 学习路径建议

1. 基础:给函数加类型提示
 ↓
2. 进阶:容器类型、Optional、Union
 ↓
3. 实战:配合 IDE/mypy 检查项目
 ↓
4. 高级:泛型、Protocol、TypedDict
 ↓
5. 深入:自定义泛型类、协变逆变(Covariant/Contravariant)

1.8 思考题

from typing import TypeVar, Generic

T = TypeVar('T')
S = TypeVar('S', bound=int)

class Container(Generic[T]):
  def __init__(self, value: T) -> None:
      self.value = value
  
  def get(self) -> T:
      return self.value

# 下面哪些用法是正确的?IDE/mypy 会怎么提示?
c1 = Container(42)          # 类型推断为 Container[int]
c2: Container[str] = Container(42) # ?
c3 = Container[int](3.14)   # ?

# 如果改成 S 呢?
class IntContainer(Generic[S]): ...
答案
  • c2错误,声明 Container[str] 但传入 int
  • c3运行时通过,但类型检查警告float 不是 int
  • S = TypeVar('S', bound=int) 限制 S 必须是 int 或其子类,float 不符合

  • 掌握 typing 能让你的 Python 代码在团队协作和大型项目中更加可靠。建议配合 VS Code + PylancePyCharm 使用,获得即时的类型检查和智能提示!

X 参考文献

posted @ 2026-04-09 12:22  千千寰宇  阅读(7)  评论(0)    收藏  举报