魔术方法完全参考

魔术方法(Magic Methods),也称双下方法(Dunder Methods),是 Python 中以双下划线开头和结尾的特殊方法。Python 解释器在特定操作时自动调用这些方法,通过实现它们可以让自定义类与 Python 的内置语法和协议无缝集成。 创建实例时最先调用的静态方法,负责分配内存并返回新实例。 返回值:返回新创建的实例(通常是 cls 的实例)。若不返回 cls 的实例,则不会调用 __init__。 触发时机:调用 MyClass(...) 时,在 __init__ 之前调用。 实例初始化方法,负责设置实例属性。 返回值:必须返回

分享

官方文档:https://docs.python.org/3/reference/datamodel.html
适用版本:Python 3.12(2026-05-08 核实)

魔术方法(Magic Methods),也称双下方法(Dunder Methods),是 Python 中以双下划线开头和结尾的特殊方法。Python 解释器在特定操作时自动调用这些方法,通过实现它们可以让自定义类与 Python 的内置语法和协议无缝集成。


对象生命周期

__new__

创建实例时最先调用的静态方法,负责分配内存并返回新实例。

参数 类型 说明
cls type 要创建实例的类
*args any 传给 __init__ 的位置参数
**kwargs any 传给 __init__ 的关键字参数

返回值:返回新创建的实例(通常是 cls 的实例)。若不返回 cls 的实例,则不会调用 __init__

触发时机:调用 MyClass(...) 时,在 __init__ 之前调用。

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):
        self.value = value

a = Singleton(1)
b = Singleton(2)
print(a is b)     # True
print(a.value)    # 2(__init__ 被调用了两次)

# 不可变类型的子类化必须在 __new__ 中修改值
class UpperStr(str):
    def __new__(cls, value):
        return super().__new__(cls, value.upper())

s = UpperStr('hello')
print(s)  # 'HELLO'

__init__

实例初始化方法,负责设置实例属性。

参数 类型 说明
self instance 已由 __new__ 创建的实例
*args any 构造函数的位置参数
**kwargs any 构造函数的关键字参数

返回值:必须返回 None,否则抛出 TypeError

触发时机__new__ 返回 cls 实例后立即调用。

class Point:
    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

p = Point(1.0, 2.0)

__del__

对象被垃圾回收时调用,也称析构方法。

参数 类型 说明
self instance 即将被销毁的实例

返回值:无。

触发时机:对象引用计数降为 0 时(CPython)。注意:时机不确定,程序退出时可能不被调用。

class Resource:
    def __init__(self, name):
        self.name = name
        print(f"Acquired: {self.name}")

    def __del__(self):
        print(f"Released: {self.name}")

r = Resource("file_handle")
del r  # 打印 "Released: file_handle"

# 注意:不要依赖 __del__ 释放关键资源,应使用上下文管理器

__init_subclass__

当该类被继承时调用,可用于自动注册子类或强制接口约束。

参数 类型 说明
cls type 新创建的子类
**kwargs any 传给该方法的关键字参数

返回值:无。

触发时机:定义子类时(类创建阶段),而非实例化时。

class Plugin:
    _registry = {}

    def __init_subclass__(cls, plugin_name=None, **kwargs):
        super().__init_subclass__(**kwargs)
        if plugin_name:
            Plugin._registry[plugin_name] = cls
            print(f"Registered plugin: {plugin_name}")

class AuthPlugin(Plugin, plugin_name='auth'):
    pass

class LogPlugin(Plugin, plugin_name='log'):
    pass

print(Plugin._registry)
# {'auth': <class 'AuthPlugin'>, 'log': <class 'LogPlugin'>}

字符串表示

__repr__

返回对象的"官方"字符串表示,目标是能通过 eval() 重新构造对象(尽力而为)。

参数 类型 说明
self instance 当前实例

返回值str

触发时机repr(obj)、交互式解释器显示、f"{obj!r}"、容器打印时对元素调用。

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Point({self.x!r}, {self.y!r})"

p = Point(1, 2)
print(repr(p))   # Point(1, 2)
print([p])       # [Point(1, 2)](列表打印元素时用 repr)

__str__

返回对象的可读字符串表示,面向用户展示。

参数 类型 说明
self instance 当前实例

返回值str

触发时机str(obj)print(obj)f"{obj}"f"{obj!s}"。未定义时回退到 __repr__

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Point({self.x!r}, {self.y!r})"

    def __str__(self):
        return f"({self.x}, {self.y})"

p = Point(1, 2)
print(str(p))    # (1, 2)
print(repr(p))   # Point(1, 2)

__format__

支持 format() 内置函数和 f-string 的格式规范。

参数 类型 说明
self instance 当前实例
format_spec str 格式规范字符串(如 .2f>10 等),空字符串表示无格式要求

返回值str

触发时机format(obj, spec)f"{obj:spec}"

class Money:
    def __init__(self, amount, currency='CNY'):
        self.amount = amount
        self.currency = currency

    def __format__(self, spec):
        if spec == 'short':
            return f"{self.amount:.0f}"
        elif spec == 'full':
            return f"{self.currency} {self.amount:,.2f}"
        return f"{self.amount:{spec}}" if spec else str(self.amount)

m = Money(1234.5)
print(f"{m:full}")   # CNY 1,234.50
print(f"{m:short}")  # 1235
print(f"{m:.2f}")    # 1234.50

__bytes__

返回对象的字节串表示。

参数 类型 说明
self instance 当前实例

返回值bytes

触发时机bytes(obj)

class Packet:
    def __init__(self, data: str):
        self.data = data

    def __bytes__(self):
        return self.data.encode('utf-8')

p = Packet('hello')
print(bytes(p))  # b'hello'

比较运算

比较方法总览

方法 运算符 触发时机
__eq__(self, other) == a == b
__ne__(self, other) != a != b(未定义时默认取反 __eq__
__lt__(self, other) < a < b
__le__(self, other) <= a <= b
__gt__(self, other) > a > b
__ge__(self, other) >= a >= b

每个比较方法的参数:

参数 类型 说明
self instance 运算符左侧对象
other any 运算符右侧对象

返回值:通常返回 bool,但可以返回任意值(NumPy 返回数组)。当无法与 other 比较时应返回 NotImplemented(不是 NotImplementedError),Python 会尝试调用 other 的反射方法。

from functools import total_ordering

@total_ordering
class Version:
    def __init__(self, major, minor, patch):
        self.major = major
        self.minor = minor
        self.patch = patch

    def _tuple(self):
        return (self.major, self.minor, self.patch)

    def __eq__(self, other):
        if not isinstance(other, Version):
            return NotImplemented
        return self._tuple() == other._tuple()

    def __lt__(self, other):
        if not isinstance(other, Version):
            return NotImplemented
        return self._tuple() < other._tuple()

    def __repr__(self):
        return f"Version({self.major}, {self.minor}, {self.patch})"

v1 = Version(1, 2, 0)
v2 = Version(1, 3, 0)
print(v1 < v2)   # True
print(v1 > v2)   # False(total_ordering 自动生成)
print(sorted([v2, v1]))  # [Version(1, 2, 0), Version(1, 3, 0)]

__hash__

返回对象的哈希值,使对象可用于集合或字典键。

参数 类型 说明
self instance 当前实例

返回值int

触发时机hash(obj)、将对象放入 set 或用作 dict 键。

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        return isinstance(other, Point) and self.x == other.x and self.y == other.y

    def __hash__(self):
        # 基于值的哈希,与 __eq__ 保持一致
        return hash((self.x, self.y))

p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1 == p2)   # True
print(hash(p1) == hash(p2))  # True
print({p1, p2})   # {Point(1, 2)}(只有一个元素)

重要陷阱:定义 __eq__ 后,Python 自动将 __hash__ 设为 None,使对象不可哈希。若要对象同时支持相等比较和哈希,必须显式定义 __hash__

class BadPoint:
    def __eq__(self, other):
        return True  # 定义了 __eq__ 但没定义 __hash__

p = BadPoint()
hash(p)  # TypeError: unhashable type: 'BadPoint'

数值运算

二元运算符

方法 运算符 说明
__add__(self, other) + 加法
__sub__(self, other) - 减法
__mul__(self, other) * 乘法
__truediv__(self, other) / 真除法
__floordiv__(self, other) // 整除
__mod__(self, other) % 取模
__pow__(self, other) ** 幂运算
__matmul__(self, other) @ 矩阵乘法

每个方法参数相同:

参数 类型 说明
self instance 左操作数
other any 右操作数

返回值:运算结果,无法处理时返回 NotImplemented

反射运算符(右操作数版本)

当左操作数不支持该运算时,Python 尝试调用右操作数的反射版本:

方法 对应正向方法
__radd__(self, other) __add__
__rsub__(self, other) __sub__
__rmul__(self, other) __mul__
__rtruediv__(self, other) __truediv__
__rfloordiv__(self, other) __floordiv__
__rmod__(self, other) __mod__
__rpow__(self, other) __pow__

原地运算符

方法 运算符 说明
__iadd__(self, other) += 原地加法
__isub__(self, other) -= 原地减法
__imul__(self, other) *= 原地乘法
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        if isinstance(other, Vector):
            return Vector(self.x + other.x, self.y + other.y)
        return NotImplemented

    def __radd__(self, other):
        # 支持 0 + Vector(sum() 从 0 开始累加时需要此方法)
        if other == 0:
            return self
        return NotImplemented

    def __mul__(self, scalar):
        if isinstance(scalar, (int, float)):
            return Vector(self.x * scalar, self.y * scalar)
        return NotImplemented

    def __rmul__(self, scalar):
        return self.__mul__(scalar)

    def __iadd__(self, other):
        if isinstance(other, Vector):
            self.x += other.x
            self.y += other.y
            return self  # 原地修改,返回 self
        return NotImplemented

    def __neg__(self):
        return Vector(-self.x, -self.y)

    def __abs__(self):
        import math
        return math.sqrt(self.x**2 + self.y**2)

    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

v1 = Vector(1, 2)
v2 = Vector(3, 4)

print(v1 + v2)      # Vector(4, 6)
print(v1 * 3)       # Vector(3, 6)
print(3 * v1)       # Vector(3, 6)(通过 __rmul__)
print(-v1)          # Vector(-1, -2)
print(abs(v2))      # 5.0
print(sum([v1, v2]))  # Vector(4, 6)(sum 从 0 开始,触发 __radd__)

v1 += v2
print(v1)  # Vector(4, 6)

一元运算符

方法 运算符 说明 返回值
__neg__(self) -obj 取负 新对象
__pos__(self) +obj 取正 新对象
__abs__(self) abs(obj) 绝对值 数值
__invert__(self) ~obj 按位取反 整数

容器协议

__len__

返回容器的元素数量。

参数 类型 说明
self instance 当前实例

返回值:非负整数。

触发时机len(obj),也被 bool(obj) 用于真值判断(若未定义 __bool__)。


__getitem____setitem____delitem__

实现下标访问、赋值和删除。

参数 类型 说明
self instance 当前实例
key any 索引或键,可以是整数、切片、字符串等
value(仅 __setitem__ any 要设置的值

触发时机obj[key]obj[key] = valuedel obj[key]


__contains__

实现 in 运算符。

参数 类型 说明
self instance 当前实例
item any 要查找的元素

返回值bool。未定义时 Python 会遍历迭代器逐一比较。


__iter____next__

实现迭代器协议。

方法 参数 返回值 说明
__iter__(self) self iterator 返回迭代器对象(通常返回 self)
__next__(self) self any 返回下一个值,耗尽时抛 StopIteration

触发时机iter(obj) 触发 __iter__next(obj) 触发 __next__for 循环自动调用两者。


__reversed__

支持 reversed() 内置函数。

参数 类型 说明
self instance 当前实例

返回值:逆序迭代器。

class Playlist:
    def __init__(self, songs):
        self._songs = list(songs)

    def __len__(self):
        return len(self._songs)

    def __getitem__(self, index):
        return self._songs[index]

    def __setitem__(self, index, value):
        self._songs[index] = value

    def __delitem__(self, index):
        del self._songs[index]

    def __contains__(self, song):
        return song in self._songs

    def __iter__(self):
        return iter(self._songs)

    def __reversed__(self):
        return reversed(self._songs)

    def __repr__(self):
        return f"Playlist({self._songs!r})"

pl = Playlist(['A', 'B', 'C'])
print(len(pl))           # 3
print(pl[0])             # 'A'
print('B' in pl)         # True
print(list(reversed(pl)))  # ['C', 'B', 'A']

for song in pl:
    print(song)          # A, B, C

属性访问

__getattr__

仅在正常属性查找失败时调用(属性不存在时的兜底方法)。

参数 类型 说明
self instance 当前实例
name str 属性名

返回值:属性值,或抛出 AttributeError

触发时机:访问不存在的属性时。不会拦截已存在属性的访问。


__getattribute__

拦截所有属性访问,包括已存在的属性。

参数 类型 说明
self instance 当前实例
name str 属性名

返回值:属性值,或抛出 AttributeError

触发时机:任何属性访问都会触发,包括 self.x


__setattr____delattr__

拦截属性赋值和删除。

参数 类型 说明
self instance 当前实例
name str 属性名
value(仅 __setattr__ any 要设置的值

触发时机obj.attr = valuedel obj.attr


__dir__

自定义 dir() 的返回值。

参数 类型 说明
self instance 当前实例

返回值:属性名列表(可迭代对象)。

class DynamicProxy:
    def __init__(self, target):
        object.__setattr__(self, '_target', target)

    def __getattr__(self, name):
        # 只在属性不存在时调用,代理到目标对象
        return getattr(self._target, name)

    def __getattribute__(self, name):
        # 拦截所有访问
        if name.startswith('_'):
            return object.__getattribute__(self, name)
        print(f"Accessing: {name}")
        return object.__getattribute__(self, name)

    def __setattr__(self, name, value):
        if name.startswith('_'):
            object.__setattr__(self, name, value)
        else:
            print(f"Setting: {name} = {value}")
            object.__setattr__(self, name, value)

    def __dir__(self):
        base = list(object.__dir__(self))
        target_attrs = dir(self._target)
        return sorted(set(base + target_attrs))

# 关键:在 __getattribute__ 和 __setattr__ 中必须用 object.__xxx__ 访问自身属性
# 否则会导致无限递归

__getattr__ vs __getattribute__ 的核心区别

特性 __getattr__ __getattribute__
触发条件 属性不存在时 所有属性访问
用途 动态属性、代理 全面拦截、访问控制
无限递归风险 高(内部访问必须用 object.__getattribute__
性能开销 低(仅失败时触发) 高(每次访问都触发)

描述符

描述符是定义了 __get____set____delete__ 的对象,通过类属性的方式管理实例属性。

__get__

参数 类型 说明
self descriptor 描述符实例
obj instance / None 访问该描述符的实例,通过类访问时为 None
objtype type 实例的类(或直接访问的类)

返回值:属性值。

__set__

参数 类型 说明
self descriptor 描述符实例
obj instance 属性被设置的实例
value any 要设置的值

返回值:无(None)。

__delete__

参数 类型 说明
self descriptor 描述符实例
obj instance 属性被删除的实例

返回值:无(None)。

__set_name__

类创建时调用,告知描述符其在类中的属性名。

参数 类型 说明
self descriptor 描述符实例
owner type 拥有该描述符的类
name str 描述符在类中的属性名
class Validator:
    """带类型验证的描述符"""

    def __set_name__(self, owner, name):
        self.name = name
        self.private_name = f'_{name}'

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self  # 通过类访问时返回描述符本身
        return getattr(obj, self.private_name, None)

    def __set__(self, obj, value):
        self.validate(value)
        setattr(obj, self.private_name, value)

    def validate(self, value):
        pass  # 子类实现

class PositiveInt(Validator):
    def validate(self, value):
        if not isinstance(value, int) or value <= 0:
            raise ValueError(f"{self.name} must be a positive integer, got {value!r}")

class Person:
    age = PositiveInt()

    def __init__(self, name, age):
        self.name = name
        self.age = age  # 触发 PositiveInt.__set__

p = Person('Alice', 30)
print(p.age)   # 30

try:
    p.age = -1  # ValueError: age must be a positive integer, got -1
except ValueError as e:
    print(e)

# 通过类访问返回描述符本身
print(Person.age)  # <__main__.PositiveInt object at ...>

描述符分类

类型 条件 说明
数据描述符 定义了 __set____delete__ 优先级高于实例 __dict__
非数据描述符 只定义了 __get__ 优先级低于实例 __dict__

上下文管理器

__enter____exit__

实现 with 语句协议。

__enter__ 参数:

参数 类型 说明
self instance 上下文管理器实例

返回值:绑定到 as 子句变量的值(无 as 时不使用)。

__exit__ 参数:

参数 类型 说明
self instance 上下文管理器实例
exc_type type / None 异常类型,无异常时为 None
exc_val Exception / None 异常实例,无异常时为 None
exc_tb traceback / None 异常的 traceback 对象,无异常时为 None

返回值:真值表示吞掉(抑制)异常;假值(或 None)表示继续传播异常。

import time

class Timer:
    def __enter__(self):
        self.start = time.perf_counter()
        return self  # as 子句拿到 self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.elapsed = time.perf_counter() - self.start
        if exc_type is not None:
            print(f"Exception occurred after {self.elapsed:.4f}s: {exc_val}")
        else:
            print(f"Elapsed: {self.elapsed:.4f}s")
        return False  # 不抑制异常

with Timer() as t:
    total = sum(range(1_000_000))

print(f"Sum: {total}, Time: {t.elapsed:.4f}s")

# 抑制特定异常
class SuppressZeroDivision:
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        return exc_type is ZeroDivisionError  # 只抑制 ZeroDivisionError

with SuppressZeroDivision():
    result = 1 / 0  # 不会抛出异常
print("Continued normally")

可调用对象

__call__

使实例可像函数一样被调用。

参数 类型 说明
self instance 当前实例
*args any 调用时传入的位置参数
**kwargs any 调用时传入的关键字参数

返回值:任意值。

触发时机obj(...)

class Multiplier:
    def __init__(self, factor):
        self.factor = factor

    def __call__(self, x):
        return x * self.factor

double = Multiplier(2)
triple = Multiplier(3)

print(double(5))   # 10
print(triple(5))   # 15

# 可调用对象与函数的区别:可以携带状态
class Counter:
    def __init__(self):
        self.count = 0

    def __call__(self, *args, **kwargs):
        self.count += 1
        return self.count

c = Counter()
print(c())  # 1
print(c())  # 2
print(c.count)  # 2

# 检测可调用性
print(callable(double))   # True
print(callable(42))       # False

类型系统

__class_getitem__

支持泛型类型参数语法 MyClass[type]

参数 类型 说明
cls type 类本身
item any 方括号内的类型参数

返回值:通常返回 types.GenericAlias 或自定义对象。

触发时机MyClass[int]MyClass[str, int]

class Stack:
    def __class_getitem__(cls, item):
        return f"Stack[{item.__name__}]"

print(Stack[int])   # Stack[int]
print(Stack[str])   # Stack[str]

# 实际用于类型注解
from typing import Generic, TypeVar
T = TypeVar('T')

class TypedStack(Generic[T]):
    def __init__(self):
        self._data: list[T] = []

    def push(self, item: T) -> None:
        self._data.append(item)

    def pop(self) -> T:
        return self._data.pop()

stack: TypedStack[int] = TypedStack()

__instancecheck____subclasscheck__

自定义 isinstance()issubclass() 的行为,定义在元类上。

方法 参数 触发时机
__instancecheck__(cls, instance) cls: 类, instance: 待检测对象 isinstance(instance, cls)
__subclasscheck__(cls, subclass) cls: 类, subclass: 待检测类 issubclass(subclass, cls)
class SupportsAddMeta(type):
    def __instancecheck__(cls, instance):
        return hasattr(instance, '__add__')

    def __subclasscheck__(cls, subclass):
        return hasattr(subclass, '__add__')

class SupportsAdd(metaclass=SupportsAddMeta):
    pass

print(isinstance(1, SupportsAdd))      # True(int 有 __add__)
print(isinstance('a', SupportsAdd))    # True(str 有 __add__)
print(isinstance(None, SupportsAdd))   # False(None 没有 __add__)

序列化

__getstate____setstate__

自定义对象的序列化和反序列化行为,用于 pickle

__getstate__ 参数:

参数 类型 说明
self instance 当前实例

返回值:任意可序列化对象(通常是字典),表示对象状态。

__setstate__ 参数:

参数 类型 说明
self instance 当前实例(尚未初始化)
state any __getstate__ 的返回值

返回值:无。

import pickle

class Connection:
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self._socket = None  # 不可序列化的资源

    def connect(self):
        self._socket = f"<socket to {self.host}:{self.port}>"

    def __getstate__(self):
        # 不序列化 socket 连接
        state = self.__dict__.copy()
        state['_socket'] = None
        return state

    def __setstate__(self, state):
        self.__dict__.update(state)
        # 反序列化后不自动重连

conn = Connection('localhost', 5432)
conn.connect()
print(conn._socket)   # <socket to localhost:5432>

serialized = pickle.dumps(conn)
restored = pickle.loads(serialized)
print(restored._socket)  # None(socket 未被序列化)
print(restored.host)     # 'localhost'

__reduce__

提供完整的序列化控制,返回重建对象所需的信息。

参数 类型 说明
self instance 当前实例

返回值:元组 (callable, args)(callable, args, state, ...),callable(*args) 用于重建对象。

class MyObj:
    def __init__(self, data):
        self.data = data

    def __reduce__(self):
        return (self.__class__, (self.data,))

踩坑与注意事项

1. __eq__ 重写后 __hash__ 自动变 None

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        return (self.x, self.y) == (other.x, other.y)
    # 没有定义 __hash__!

p = Point(1, 2)
hash(p)  # TypeError: unhashable type: 'Point'

{p: 'origin'}  # TypeError: unhashable type: 'Point'

# 修复:显式定义 __hash__
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        return (self.x, self.y) == (other.x, other.y)

    def __hash__(self):
        return hash((self.x, self.y))

# 若要声明对象不可哈希(可变对象的最佳实践):
# __hash__ = None

2. __getattr____getattribute__ 的无限递归

class BadProxy:
    def __getattribute__(self, name):
        # 错误:self.data 会再次触发 __getattribute__,导致无限递归
        return self.data[name]  # RecursionError!

class GoodProxy:
    def __getattribute__(self, name):
        # 正确:使用 object.__getattribute__ 访问自身属性
        data = object.__getattribute__(self, 'data')
        return data[name]

3. __del__ 的不确定性

class Resource:
    def __del__(self):
        # 不要在这里做关键清理
        # 1. 循环引用时可能不被调用
        # 2. 程序崩溃时不被调用
        # 3. 解释器退出时调用顺序不确定
        pass

# 正确做法:使用上下文管理器
class Resource:
    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.cleanup()  # 确保被调用

4. __exit__ 返回值容易搞错

class BadContextManager:
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        # 错误:无论是否有异常,总是返回 True,会吞掉所有异常
        return True

class GoodContextManager:
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is ValueError:
            print(f"Suppressed ValueError: {exc_val}")
            return True   # 仅抑制 ValueError
        return False      # 其他异常正常传播

5. __new__ 返回不同类型时不调用 __init__

class MyClass:
    def __new__(cls, value):
        if value < 0:
            return None  # 返回非 cls 实例
        return super().__new__(cls)

    def __init__(self, value):
        print("__init__ called")  # 当 value < 0 时不会执行
        self.value = value

obj = MyClass(-1)
print(obj)   # None,__init__ 未被调用

6. 描述符只对类属性有效

class MyDescriptor:
    def __get__(self, obj, objtype=None):
        return 42

class MyClass:
    attr = MyDescriptor()  # 类属性,描述符生效

obj = MyClass()
print(obj.attr)         # 42(通过描述符)

obj.__dict__['attr'] = 100  # 直接写入实例字典
# 数据描述符(有 __set__):obj.attr 仍返回 42(描述符优先)
# 非数据描述符(无 __set__):obj.attr 返回 100(实例字典优先)

最佳实践

实现 __eq__ 必须同时实现 __hash__:Python 3 中定义 __eq__ 后,__hash__ 会被自动设为 None(对象变为不可哈希),无法作为 dict key 或 set 元素。可变对象显式设 __hash__ = None,不可变对象同时实现两者并保证 hash 一致性。

__repr__ 应返回可重现对象的字符串:理想格式是 ClassName(arg1=val1, arg2=val2),能被 eval() 重新创建对象;至少要包含足够信息让开发者调试定位问题。

__slots__ 减少内存占用:大量实例时,__slots__ 替代 __dict__ 存储实例属性,内存节省 20-50%;但 __slots__ 不支持默认实例属性,需要在 __init__ 中初始化所有槽。

__enter__ / __exit__ 成对实现:只要类实现这两个方法,就能用于 with 语句,无需继承任何基类;__exit__ 返回 True 时抑制异常,通常应返回 None / False

谨慎使用 __getattr__ vs __getattribute____getattr__ 只在属性查找失败时调用(安全),__getattribute__ 在任何属性访问时调用(容易无限递归);在 __getattribute__ 中访问 self 属性必须用 object.__getattribute__(self, name)


常见陷阱

陷阱:__del__ 不等于析构函数,不可靠

现象: 期望 __del__ 在对象销毁时释放资源,但资源没有被及时释放,或程序退出时才执行。
原因: __del__ 依赖 CPython 的引用计数,存在循环引用时无法预测调用时机,解释器退出时调用顺序也不确定。
解决: 资源管理用上下文管理器(with)和 __enter__/__exit__,或显式调用 close() 方法。

陷阱:__init_subclass__ 替代元类

现象: 用元类(metaclass)拦截子类定义时代码复杂难维护。
原因: 很多元类使用场景可以用 Python 3.6+ 的 __init_subclass__ 实现,更简洁。
解决: 当需要在子类定义时执行逻辑时,优先考虑 __init_subclass__,只有需要拦截 type() 调用本身时才用元类。

陷阱:__iadd__ 返回值决定变量是否重绑定

现象: list += [1] 不改变变量绑定(原地修改),但 tuple += (1,) 创建新对象并重绑定变量。
原因: += 调用 __iadd__,若返回 self(如 list),变量不重绑定;若返回新对象(如 tuple),变量重绑定到新对象。
解决: 实现 __iadd__ 时原地修改并 return self,保持与内置 list 行为一致。


参见

装饰器与函数高级
itertools与functools完全指南
内置函数完全参考

阅读更多

Web 安全基础

1. HTML 转义(服务端渲染必须): 2. CSP(Content Security Policy): 3. HttpOnly Cookie:防止 JS 读取会话 Cookie: 4. 前端框架防护: 攻击者在第三方网站构造一个表单,诱导已登录用户提交,浏览器会自动携带目标站的 Cookie。 触发条件: 1. 用户已登录目标网站(Cookie 有效) 2. 目标 API 仅凭 Cookie 识别用户身份 3. 请求来源未验证 1. CSRF Token(推荐): 2. SameSite Cookie: 3. 验证 Origin/Referer 头:

By yellowdog

HTTP 协议深度指南

HTTP(HyperText Transfer Protocol)是 Web 的基础传输协议,基于 TCP/IP,采用请求/响应模型。 相关文档:Web安全基础(/web-an-quan-ji-chu/) FastAPI完全指南(/fastapi-wan-quan-zhi-nan/) Nginx完全指南(/nginx-wan-quan-zhi-nan/) 幂等性:多次执行相同请求,服务器状态结果相同。PUT /users/1 多次执行结果一致;POST /users 每次创建新资源,非幂等。 浏览器直接从本地缓存读取,不向服务器发送请求。 缓存命中时,状

By yellowdog

系统设计基础

SLA 对照表: 选择建议:无状态服务(Web 层、API 层)优先水平扩展;数据库初期垂直扩展,达到瓶颈后考虑分库分表或读写分离。 缓存穿透(查询不存在的 key,每次都打到 DB): 缓存击穿(热点 key 过期,瞬间大量请求打到 DB): 缓存雪崩(大量 key 同时过期,或缓存服务宕机): 令牌桶 Python 实现: Redis 实现分布式限流(滑动窗口): URL 命名规则: Cursor 分页响应格式: 雪花算法结构(64 bit): 定义:分布式系统不能同时满足以下三个特性: 在分布式环境中 P 是必须保证的,所以实际是 CP vs AP

By yellowdog

算法思路与模板

二分查找要求序列有序,每次将搜索范围缩减一半,时间复杂度 O(log n)。 两个指针从两端向中间收缩,常用于有序数组。 滑动窗口维护一个满足条件的区间 left, right,right 不断向右扩张,条件不满足时收缩 left。 滑动窗口通用框架: 1. 确定"子问题":原问题可以分解为哪些规模更小的同类问题 2. 定义 dpi 或 dpij 的含义,要足够清晰 3. 推导状态转移方程 4. 确定初始状态(边界条件) 5. 确定计算顺序(确保依赖的子问题先计算) 每件物品最多选一次。dpj = 容量为 j 时的最大价值,逆序遍历容量防止重复选取。 每

By yellowdog