Python 面向对象
1. new vs init
python
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, value=None):
self.value = value
a = Singleton('first')
b = Singleton('second')
print(a is b) # True, 同一实例
print(a.value) # second, __init__ 被调用了两次执行顺序:
ClassName(args) → __new__(cls, args) → __init__(self, args)
返回新实例 初始化该实例| 特性 | __new__ | __init__ |
|---|---|---|
| 调用时机 | 实例创建前 | 实例创建后 |
| 参数 | cls (类本身) | self (新实例) |
| 返回值 | 必须返回实例 | 必须返回 None |
| 用途 | 控制实例创建(单例、不可变子类) | 初始化实例属性 |
python
# __new__ 用于不可变类型的子类化
class PositiveInt(int):
def __new__(cls, value):
return super().__new__(cls, abs(value))
x = PositiveInt(-5)
print(x, type(x)) # 5 <class '__main__.PositiveInt'>2. MRO 与 C3 线性化
Python 使用 C3 线性化算法 确定方法解析顺序(MRO):
python
class A:
def greet(self):
return 'A'
class B(A):
def greet(self):
return 'B'
class C(A):
def greet(self):
return 'C'
class D(B, C):
pass
print(D.mro())
# [<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>]
d = D()
print(d.greet()) # B, 按 MRO 顺序查找C3 线性化规则:
- 子类优先于父类
- 多个父类按声明顺序
- 保持局部优先级(不违反任何基类的线性化顺序)
python
# C3 的约束验证
class X:
def method(self):
return 'X'
class Y(X):
def method(self):
return 'Y'
class Z(X):
def method(self):
return 'Z'
class W(Y, Z):
pass
# W.mro() = [W, Y, Z, X, object]
# Y 在 Z 前(声明顺序),Z 在 X 前(继承顺序)
print(W().method()) # Ypython
# 不合法的 MRO —— 会抛出 TypeError
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
class E(C, B): # 与 D 中 B 在 C 前矛盾
pass
# class F(D, E): # TypeError: Cannot create a consistent method resolution order
# pass3. 元类 — type
python
# type 是所有类的类
print(type(42)) # <class 'int'>
print(type(int)) # <class 'type'>
print(type(type)) # <class 'type'>
# type() 的动态创建能力
# type(name, bases, dict) -> new class
def init_method(self, name):
self.name = name
def repr_method(self):
return f'Person({self.name})'
Person = type('Person', (), {
'__init__': init_method,
'__repr__': repr_method,
})
p = Person('Alice')
print(p) # Person(Alice)自定义元类
python
class RegistryMeta(type):
_registry = {}
def __new__(mcs, name, bases, namespace):
cls = super().__new__(mcs, name, bases, namespace)
if bases: # 跳过基类本身
mcs._registry[name] = cls
return cls
@classmethod
def get_registry(mcs):
return mcs._registry.copy()
class Plugin(metaclass=RegistryMeta):
pass
class AuthPlugin(Plugin):
pass
class CachePlugin(Plugin):
pass
print(RegistryMeta.get_registry())
# {'AuthPlugin': <class 'AuthPlugin'>, 'CachePlugin': <class 'CachePlugin'>}4. init_subclass
Python 3.6+ 的 __init_subclass__ 可以替代元类的部分功能:
python
class PluginBase:
_plugins = {}
def __init_subclass__(cls, plugin_name=None, **kwargs):
super().__init_subclass__(**kwargs)
name = plugin_name or cls.__name__
PluginBase._plugins[name] = cls
class Auth(PluginBase, plugin_name='auth'):
pass
class Cache(PluginBase, plugin_name='cache'):
pass
print(PluginBase._plugins)
# {'auth': <class 'Auth'>, 'cache': <class 'Cache'>}__init_subclass__ vs 元类:
| 特性 | __init_subclass__ | 元类 |
|---|---|---|
| 复杂度 | 低 | 高 |
| 能力 | 子类注册、参数验证 | 完全控制类的创建过程 |
| 推荐度 | 优先使用 | 仅在需要深度控制时使用 |
5. 描述符协议 — 数据描述符 vs 面数据描述符
python
# 描述符协议:实现了以下任一方法的对象
# __get__(self, obj, objtype=None)
# __set__(self, obj, value)
# __delete__(self, obj)
# 数据描述符:同时实现 __get__ 和 __set__(优先级更高)
class Validated:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, obj, objtype=None):
if obj is None:
return self
return obj.__dict__.get(self.name)
def __set__(self, obj, value):
if not isinstance(value, (int, float)):
raise TypeError(f'{self.name} must be numeric')
obj.__dict__[self.name] = value
class Temperature:
celsius = Validated()
fahrenheit = Validated()
def __init__(self, celsius):
self.celsius = celsius
@property
def fahrenheit(self):
return self.celsius * 9 / 5 + 32
@fahrenheit.setter
def fahrenheit(self, value):
self.celsius = (value - 32) * 5 / 9
t = Temperature(100)
print(t.fahrenheit) # 212.0
# t.celsius = 'hot' # TypeError: celsius must be numeric属性查找优先级:
数据描述符 > 实例 __dict__ > 非数据描述符 > 类属性python
# 非数据描述符:只实现 __get__
class Method:
def __init__(self, func):
self.func = func
def __get__(self, obj, objtype=None):
if obj is None:
return self.func
return lambda *args, **kwargs: self.func(obj, *args, **kwargs)
class MyClass:
@Method
def greet(self):
return 'hello'
print(MyClass().greet()) # hello6. property 实现原理
property 是一个数据描述符:
python
# property 实际上是一个类
class property:
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
self.__doc__ = doc or (fget.__doc__ if fget else None)
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(obj)
def __set__(self, obj, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(obj, value)
def __delete__(self, obj):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(obj)
# 装饰器语法
def getter(self, fget):
return type(self)(fget, self.fset, self.fdel, self.__doc__)
def setter(self, fset):
return type(self)(self.fget, fset, self.fdel, self.__doc__)
def deleter(self, fdel):
return type(self)(self.fget, self.fset, fdel, self.__doc__)python
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value < 0:
raise ValueError('radius must be non-negative')
self._radius = value
@property
def area(self):
import math
return math.pi * self._radius ** 2
c = Circle(5)
print(c.area) # 78.53981633974483
c.radius = 10
print(c.area) # 314.15926535897937. 类方法 / 实例方法 / 静态方法
python
class MyClass:
class_var = 'class'
def instance_method(self):
"""第一个参数是实例 (self)"""
return f'instance: {self}, class_var: {self.class_var}'
@classmethod
def class_method(cls):
"""第一个参数是类 (cls)"""
return f'class: {cls}, class_var: {cls.class_var}'
@staticmethod
def static_method():
"""没有隐式参数"""
return 'static method'
obj = MyClass()
print(obj.instance_method()) # instance: <__main__.MyClass object>, ...
print(obj.class_method()) # class: <class '__main__.MyClass'>, ...
print(obj.static_method()) # static method
# 继承中的差异
class Child(MyClass):
class_var = 'child_class'
c = Child()
print(c.instance_method()) # 使用子类实例,但 class_var 查找会沿 MRO
print(c.class_method()) # cls 是 Child,不是 MyClass
print(c.static_method()) # 与父类相同8. ABC(抽象基类)
python
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
def description(self):
return f'{self.__class__.__name__} with area {self.area():.2f}'
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
import math
return math.pi * self.radius ** 2
def perimeter(self):
import math
return 2 * math.pi * self.radius
# s = Shape() # TypeError: Can't instantiate abstract class
c = Circle(5)
print(c.description()) # Circle with area 78.54
# 注册(不强制实现)
from abc import ABCMeta
class MyInterface(ABCMeta):
pass
class MyABC(metaclass=MyInterface):
pass9. Mixin 模式
python
import json
class JsonMixin:
"""Mixin: 提供 JSON 序列化/反序列化能力"""
def to_json(self):
return json.dumps(self.__dict__, ensure_ascii=False, indent=2)
@classmethod
def from_json(cls, data):
return cls(**json.loads(data))
class LogMixin:
"""Mixin: 提供日志能力"""
def log(self, message):
print(f'[{self.__class__.__name__}] {message}')
class User(JsonMixin, LogMixin):
def __init__(self, name, age):
self.name = name
self.age = age
u = User('Alice', 30)
print(u.to_json())
# {
# "name": "Alice",
# "age": 30
# }
u.log('User created')
# [User] User created
# 从 JSON 恢复
u2 = User.from_json('{"name": "Bob", "age": 25}')
print(u2.name) # BobMixin 设计原则:
- 不应有自己的
__init__ - 不应与业务类产生强耦合
- 通常不直接实例化
- 放在 MRO 左侧(继承列表靠前位置)
10. dataclass
python
from dataclasses import dataclass, field, asdict, astuple
from typing import List
@dataclass
class Point:
x: float
y: float
def distance_to(self, other: 'Point') -> float:
return ((self.x - other.x)**2 + (self.y - other.y)**2) ** 0.5
p1 = Point(1.0, 2.0)
p2 = Point(4.0, 6.0)
print(p1) # Point(x=1.0, y=2.0)
print(p1 == p2) # False (自动生成 __eq__)
print(p1.distance_to(p2)) # 5.0
# 不可变 dataclass
@dataclass(frozen=True)
class FrozenPoint:
x: float
y: float
fp = FrozenPoint(1, 2)
# fp.x = 3 # FrozenInstanceError
# 更多选项
@dataclass(order=True, slots=True) # 3.10+
class Student:
name: str
grade: int = field(compare=True) # 参与比较
age: int = field(compare=False) # 不参与比较
tags: List[str] = field(default_factory=list)
s1 = Student('Alice', grade=90, age=20)
s2 = Student('Bob', grade=85, age=22)
print(s1 > s2) # True, 按 grade 比较
# 转换
print(asdict(s1)) # {'name': 'Alice', 'grade': 90, 'age': 20, 'tags': []}
print(astuple(s1)) # ('Alice', 90, 20, [])dataclass 生成的方法:
__init__:初始化__repr__:字符串表示__eq__:值比较__hash__:哈希(需frozen=True或eq=True, frozen=True)__lt__,__le__等:排序(需order=True)
面试要点:dataclass 本质上是装饰器,它通过 __init_subclass__ 在类创建时注入方法,底层仍然使用普通属性存储,与 namedtuple 不同。