在 Python 开发中,我们经常会遇到需要自定义类行为的场景。无论是重载运算符、定制属性访问,还是优化内存使用,特殊方法都扮演着关键角色。这些被双下划线包裹的方法(如__init__
、__getitem__
)如同类的 "隐藏接口",掌握它们能让我们更灵活地操控类的行为。今天,我们就来深入探讨这些特殊方法的奥秘,揭开 Python 面向对象编程的底层逻辑。
特殊方法是 Python 中以双下划线开头和结尾的方法,它们定义了类与解释器之间的交互协议。当我们对对象执行len(obj)
、obj[key]
等操作时,解释器会自动调用对应的特殊方法。例如:
len(obj)
→ obj.__len__()
obj[key]
→ obj.__getitem__(key)
x + y
→ x.__add__(y)
这种机制让自定义类可以模拟内置类型的行为,实现运算符重载、属性拦截等高级功能。如果未定义对应的特殊方法,执行相关操作时会抛出AttributeError
或TypeError
。
__new__
与__init__
:对象创建的双生子在类的生命周期中,__new__
和__init__
是创建对象的核心方法:
__new__(cls[, ...])
是一个静态方法,负责创建类的新实例,返回值必须是新对象实例__init__(self[, ...])
在实例创建后被调用,用于初始化对象状态python
class ImmutableVector:
def __new__(cls, x, y):
instance = super().__new__(cls)
instance.x = x
instance.y = y
return instance
def __init__(self, x, y):
# 这里的初始化会在__new__返回实例后执行
pass
注意:当__new__
返回的不是cls
的实例时,__init__
将不会被调用。这一特性常用于不可变类型的子类定制,如自定义int
的子类。
__del__
:析构器的陷阱与规范__del__(self)
在实例销毁时调用,但使用时需格外谨慎:
__del__
进行资源释放,解释器退出时不保证其执行__del__
无法正常调用del x
只是减少引用计数,并非直接调用__del__
python
class ResourceHolder:
def __del__(self):
print("资源释放") # 可能不会执行
__repr__(self)
:返回对象的 "官方" 字符串表示,应尽量可重建对象__str__(self)
:返回 "非正式" 字符串表示,用于 print 和 format__bytes__(self)
:返回对象的字节串表示python
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Point({self.x}, {self.y})"
def __str__(self):
return f"({self.x}, {self.y})"
Python 提供了 6 个富比较方法,对应不同的比较运算符:
__lt__
(<)、__le__
(<=)、__eq__
(==)__ne__
(!=)、__gt__
(>)、__ge__
(>=)python
class Version:
def __init__(self, major, minor):
self.major = major
self.minor = minor
def __eq__(self, other):
return self.major == other.major and self.minor == other.minor
def __lt__(self, other):
return (self.major, self.minor) < (other.major, other.minor)
关键要点:若定义了__eq__
,则需同时定义__hash__
,否则对象无法作为字典键或集合元素。
当常规属性查找失败时调用:
python
class DynamicAttr:
def __getattr__(self, name):
if name.startswith('prop_'):
return name[5:] # 返回属性名后缀
raise AttributeError(f"'{type(self).__name__}' has no attribute '{name}'")
obj.attr
找不到属性时调用__getattribute__
形成互补机制拦截所有属性访问,是属性控制的终极手段:
python
class LoggingAttr:
def __getattribute__(self, name):
print(f"Accessing attribute: {name}")
return super().__getattribute__(name)
super().__getattribute__(name)
避免无限递归__getattr__
的调用,除非显式引发AttributeError
python
class ReadOnlyAttr:
def __setattr__(self, name, value):
if name in ['readonly1', 'readonly2']:
raise AttributeError(f"Cannot set attribute {name}")
super().__setattr__(name, value)
def __delattr__(self, name):
if name in ['attr1', 'attr2']:
raise AttributeError(f"Cannot delete attribute {name}")
super().__delattr__(name)
描述器是实现了__get__
、__set__
、__delete__
的对象,用于类级别属性控制。
python
class TypedAttr:
def __init__(self, type_):
self.type_ = type_
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self.name]
def __set__(self, instance, value):
if not isinstance(value, self.type_):
raise TypeError(f"Expected {self.type_.__name__}, got {type(value).__name__}")
instance.__dict__[self.name] = value
def __set_name__(self, owner, name):
self.name = name # Python 3.6+ 钩子,设置属性名
__set__
或__delete__
,优先级高于实例属性__get__
,可被实例属性覆盖python
class NonDataDescriptor:
def __get__(self, instance, owner):
return "Non-data descriptor"
class MyClass:
desc = NonDataDescriptor()
obj = MyClass()
obj.desc # 返回"Non-data descriptor"
obj.desc = "new value" # 实例属性覆盖描述器
obj.desc # 返回"new value"
__slots__
用于显式声明实例属性,禁止创建__dict__
和__weakref__
:
python
class Point:
__slots__ = ['x', 'y'] # 声明允许的属性
def __init__(self, x, y):
self.x = x
self.y = y
__slots__
中添加__weakref__
__slots__
的兼容性在类派生时自动调用,用于定制子类行为:
python
class RegisterSubclass:
subclasses = []
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
RegisterSubclass.subclasses.append(cls)
class MyClass(RegisterSubclass):
pass
print(RegisterSubclass.subclasses) # [MyClass]
在类变量赋值时调用,常用于描述器中设置属性名:
python
class NamedAttr:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
return instance.__dict__.get(self.name, None)
元类是创建类的类,默认是type
,可定制类的创建过程:
python
class Meta(type):
def __new__(cls, name, bases, namespace):
# 添加自定义属性到类
namespace['created_by_meta'] = True
return super().__new__(cls, name, bases, namespace)
def __init__(cls, name, bases, namespace):
super().__init__(name, bases, namespace)
print(f"Class {name} created by Meta")
class MyClass(metaclass=Meta):
pass
__mro_entries__
处理非 type 基类__prepare__
创建类命名空间__new__
和__init__
元类可用于:
不可变类型定制:优先使用__new__
实现不可变类型的子类,确保实例创建时状态固定
哈希安全:当定义__eq__
时,必须同时定义__hash__
,并确保哈希值不可变
描述器应用场景:
ValidatedAttribute
)@staticmethod
和@classmethod
)__slots__
适用场景:
深入理解 Python 的特殊方法,能让我们在开发中更精准地控制类的行为,实现更优雅的设计。从基本的对象生命周期管理,到属性访问的精细控制,再到类创建过程的元编程,这些机制构成了 Python 面向对象编程的底层基石。
如果本文对你有帮助,别忘了点赞收藏,关注我,一起探索更高效的开发方式~