Python 的数据模型是基于特殊方法的,这是因为这些特殊方法提供了一种非常强大的机制,它允许自定义类型去模拟和表现得如同 Python 内置类型一样。这样的设计允许开发者用 Python 来实现几乎任何数据结构,并确保它可以与 Python 的其他部分以自然和一致的方式集成。
1.基本集合类型的接口
图中的所有类型都是抽象基类(abstract base classes)
每个顶端的抽象类都有一个特殊方法。Collection ABC统一集合需要实现的三个接口:
Iterable:支持for,拆包和其他类型的迭代
Sized: 支持内置方法 len
Container: 支持操作符 in
三个非常重要的集合类型:
Sequence:规范化内置接口,例如str,list
Mapping: 通常由dict和collections.defaultdict实现
Set: 内置类型set和frozenset的接口
其中,只有序列(Sequence)是可以被反转的(Reversible),因为序列支持随意改变内容的顺序,而字典和集合不支持。
2.使用特殊方法的两个优点
1.用户使用类时不必记住标准操作的各种各样的名称。 (“如何获取元素的数量?是 .size()、.length() 还是什么函数?”)
2.可以更加方便的利用Python 丰富的标准库并避免重新发明轮子,例如 random.choice 函数。 特殊方法使得用户自定义的对象在行为上可以和内置对象无缝对接。例如,通过实现 __len__
和 __getitem__
方法,自定义的对象可以表现得和列表或元组那样,可以使用 len()
函数和索引操作(e.g., obj[3]
)。
3.调用特殊方法
特殊方法是由 Python 解释器调用的,而不是用户。需要调用特殊方法,通常最好调用相关的内置函数(例如,len、iter、str 等)。这些内置函数调用相应的特殊方法,但通常提供额外的好处并且——对于内置类型——比方法调用更快。如下所示。
解释器在处理像 list、str、bytearray 这样的内置类型或像 NumPy 数组这样的扩展包时会走捷径。用 C 语言编写的 Python 可变大小的集合包括一个名为 PyVarObject 的struct,它有一个 ob_size 字段保存集合中的项的数量。因此,如果 my_object 是这些内置数据结构的实例,则 len(my_object) 会直接读取 ob_size 字段的值,这比调用方法要快得多。
4.特殊方法语法架构
- 集合
- 属性访问
- 迭代(包括使用async for的异步迭代)
- 运算符重载
- 函数和方法的调用
- 字符串的展示和格式化
- 使用关键字await的异步编程
- 对象的创建和销毁
- 管理器上下文(包括使用async with的异步上下文管理器)
4.1. 集合(支持 len
, getitem
, setitem
等)
通过实现 __len__
, __getitem__
, __setitem__
, 和 __contains__
,可以创建一个自定义的集合类。
class MyCollection:
def __init__(self, initial_data):
self.data = list(initial_data)
def __len__(self):
return len(self.data)
def __getitem__(self, position):
return self.data[position]
def __setitem__(self, position, value):
self.data[position] = value
def __contains__(self, item):
return item in self.data
4.2 属性访问(支持动态的属性获取和设置)
通过实现 __getattr__
, __setattr__
, 和 __delattr__
,可以控制属性的访问。
class AttributeAccess:
def __init__(self):
self._attributes = {}
def __getattr__(self, item):
return self._attributes.get(item, f"{item} not found")
def __setattr__(self, key, value):
if key == "_attributes":
super().__setattr__(key, value)
else:
self._attributes[key] = value
def __delattr__(self, item):
if item in self._attributes:
del self._attributes[item]
else:
raise AttributeError(f"{item} not found")
4.3 迭代(支持同步和异步迭代)
通过实现 __iter__
和 __next__
可以支持同步迭代。实现 __aiter__
和 __anext__
支持异步迭代。
class AsyncIterable:
def __init__(self, data):
self.data = data
async def __aiter__(self):
for item in self.data:
yield item # 使用 async for 需要异步生成器
# 使用
async def iterate_data():
async for item in AsyncIterable([1, 2, 3]):
print(item)
4.4 运算符重载
通过实现 __add__
, __mul__
, 等方法可以重载加法和乘法运算符。
class Number:
def __init__(self, value):
self.value = value
def __add__(self, other):
if isinstance(other, Number):
return Number(self.value + other.value)
return NotImplemented
def __mul__(self, other):
if isinstance(other, Number):
return Number(self.value * other.value)
return NotImplemented
4.5 函数和方法的调用
通过实现 __call__
,可以使得对象的实例可以像函数那样被调用。
class CallableObject:
def __call__(self, x):
return x * x
# 使用
obj = CallableObject()
print(obj(5)) # 输出 25
4.6 字符串的展示和格式化
通过实现 __str__
和 __repr__
控制对象的字符串表示。
class Person:
def __init__(self, name):
self.name = name
def __str__(self):
return f"Person named {self.name}"
def __repr__(self):
return f"Person({self.name!r})"
4.7 使用关键字await的异步编程
通过定义 __await__
方法可以使对象支持异步等待。
class Awaitable:
def __await__(self):
yield from asyncio.sleep(1).__await__()
return 42
# 使用
async def get_result():
result = await Awaitable()
print(result) # 输出 42
4.8 对象的创建和销毁
通过实现 __new__
和 __del__
控制对象的创建和销毁过程。
class MyClass:
def __new__(cls):
instance = super().__new__(cls)
print("Creating instance", instance)
return instance
def __del__(self):
print("Deleting instance", self)
4.9 管理器上下文(包括异步上下文管理器)
通过实现 __enter__
和 __exit__
方法,可以创建一个支持 with
语句的同步上下文管理器。对于异步上下文管理器,则需要实现 __aenter__
和 __aexit__
。
class ManagedFile:
def __init__(self, filename):
self.filename = filename
def __enter__(self):
self.file = open(self.filename, 'w')
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
if exc_type is not None:
print(f"Exception {exc_type} occurred: {exc_val}")
return True # 可以防止异常向外传播
# 使用
with ManagedFile('hello.txt') as f:
f.write('Hello, world!')