类与元编程
面试主线分三层:日常必用的 property 和 slots,提高效率的 dataclass,以及了解级的元类(能说出用途即可,不要求手写)。
property:属性即方法
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def area(self): # 读: 像属性一样访问
return 3.14 * self._radius ** 2
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value): # 写: 校验入口
if value <= 0:
raise ValueError("半径必须为正")
self._radius = value- 把方法包装成属性访问,调用方无感知(
c.area不是c.area()) - setter 是校验和副作用的最佳位置:统一在赋值处拦截非法值
- 只定义 getter 不定义 setter 就是只读属性
- 本质是装饰器 + 描述符协议
slots:省内存
class Point:
__slots__ = ("x", "y") # 固定属性名
def __init__(self, x, y):
self.x, self.y = x, y- 默认类有
__dict__(属性字典),每个实例都带一个字典,内存开销大 __slots__声明固定属性,实例用紧凑的数组存属性,省内存(大量实例时明显,如数据对象)- 代价:不能动态加属性(没有 dict)、多重继承有限制
- 适用:百万级实例的数据类。普通业务类不需要
dataclass:数据类
from dataclasses import dataclass, field
@dataclass
class User:
id: int
name: str
tags: list[str] = field(default_factory=list) # 可变默认值正确写法- 自动生成
__init__、__repr__、__eq__,减少样板代码 frozen=True生成不可变数据类(可哈希,配@dataclass(frozen=True, order=True)更全)field(default_factory=...)处理可变默认值(见函数篇的 None 陷阱同源问题)- 数据建模首选 dataclass,需要校验和序列化时换 Pydantic
元类:了解级
元类(metaclass)是“创建类的类”。class 语句的执行流程:
- 解释器调用元类(默认
type) type.__new__创建类对象(名字、基类、命名空间)type.__init__初始化类
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]知道什么场景用:在类创建时统一注入行为,如 ORM 的模型注册(SQLAlchemy Declarative)、框架的类级配置、注册表模式。业务代码几乎不用,面试答到“元类是类的类,用于类创建时拦截,典型如 ORM 注册机制”即可。
面试追问
- property 解决什么? 方法当属性访问,setter 统一校验。调用方无感知,接口稳定
- slots 有什么用? 省内存:去掉实例 dict,属性存紧凑数组。代价是不能动态加属性
- dataclass 和普通类的区别? 自动生成 init/repr/eq。frozen 不可变,default_factory 处理可变默认值
- 元类是什么? 创建类的类。class 语句由元类执行。用途:类创建时统一处理(ORM 注册、单例)
- 什么时候用元类? 框架开发。业务代码用装饰器和描述符更简单,元类是兜底手段