一、容器方法概述
1. 容器协议方法
| | | |
|---|
__len__ | | len(obj) | len(list) |
__getitem__ | | obj[key] | list[index] |
__setitem__ | | obj[key] = value | list[index] = value |
__delitem__ | | del obj[key] | del list[index] |
__iter__ | | iter(obj) | iter(list) |
__contains__ | | item in obj | item in list |
__reversed__ | | reversed(obj) | reversed(list) |
2. 容器类型分类
# 1. 不可变容器(只读)class ImmutableContainer: def __len__(self): ... def __getitem__(self, key): ... def __iter__(self): ... def __contains__(self, item): ...# 2. 可变容器(可读写)class MutableContainer(ImmutableContainer): def __setitem__(self, key, value): ... def __delitem__(self, key): ...# 3. 可哈希容器(可作为字典键)class HashableContainer(ImmutableContainer): def __hash__(self): ...
二、基础容器实现
1. 最简单的自定义列表
class SimpleList: """最简单的自定义列表""" def __init__(self, items=None): self._items = list(items) if items else [] def __len__(self): """返回长度""" return len(self._items) def __getitem__(self, index): """获取元素""" return self._items[index] def __setitem__(self, index, value): """设置元素""" self._items[index] = value def __delitem__(self, index): """删除元素""" del self._items[index] def __iter__(self): """返回迭代器""" return iter(self._items) def __contains__(self, item): """检查是否包含""" return item in self._items def __repr__(self): return f"SimpleList({self._items})"# 使用lst = SimpleList([1, 2, 3, 4, 5])print(f"长度: {len(lst)}")print(f"索引2: {lst[2]}")lst[2] = 30print(f"修改后: {lst}")del lst[2]print(f"删除后: {lst}")print(f"是否包含3: {3in lst}")print("遍历:")for item in lst: print(f" {item}")
2. 支持切片操作
class SliceableList: """支持切片的列表""" def __init__(self, items=None): self._items = list(items) if items else [] def __len__(self): return len(self._items) def __getitem__(self, index): """支持整数索引和切片""" if isinstance(index, slice): # 返回新的实例 return SliceableList(self._items[index]) return self._items[index] def __setitem__(self, index, value): """支持切片赋值""" if isinstance(index, slice): self._items[index] = value else: self._items[index] = value def __delitem__(self, index): """支持切片删除""" if isinstance(index, slice): del self._items[index] else: del self._items[index] def __repr__(self): return f"SliceableList({self._items})"# 使用lst = SliceableList([1, 2, 3, 4, 5, 6, 7, 8])print("原始:", lst)# 切片获取slice1 = lst[2:5]print("切片[2:5]:", slice1)# 切片赋值lst[1:4] = [20, 30, 40]print("切片赋值后:", lst)# 切片删除del lst[2:5]print("切片删除后:", lst)
三、__len__ 方法详解
1. 基本实现
class Playlist: """播放列表""" def __init__(self, name): self.name = name self.songs = [] def add_song(self, song): self.songs.append(song) def __len__(self): """返回歌曲数量""" return len(self.songs) def is_empty(self): """判断是否为空""" return len(self) == 0 # 调用 __len__# 使用playlist = Playlist("我的歌单")print(f"初始长度: {len(playlist)}")print(f"是否为空: {playlist.is_empty()}")playlist.add_song("稻香")playlist.add_song("青花瓷")print(f"添加后长度: {len(playlist)}")
2. 复杂长度计算
class Matrix: """矩阵类""" def __init__(self, data): self.data = data self.rows = len(data) self.cols = len(data[0]) if data else 0 def __len__(self): """返回行数""" return self.rows def __getitem__(self, index): """返回行""" return self.data[index] @property def size(self): """返回总元素数""" return self.rows * self.cols def __repr__(self): return f"Matrix({self.rows}x{self.cols})"# 使用matrix = Matrix([ [1, 2, 3], [4, 5, 6], [7, 8, 9]])print(f"行数: {len(matrix)}")print(f"列数: {matrix.cols}")print(f"总元素数: {matrix.size}")
四、__getitem__ 和 __setitem__ 详解
1. 支持多种键类型
class MultiKeyDict: """支持多种键类型的字典""" def __init__(self): self._data = {} def __getitem__(self, key): """支持多种类型的键""" print(f"获取键: {key} (类型: {type(key).__name__})") # 处理不同类型的键 if isinstance(key, tuple): # 元组键:组合查找 return [self._data.get(k) for k in key] elif isinstance(key, slice): # 切片:返回键在某个范围内的项 start, stop, step = key.indices(len(self._data)) keys = sorted(self._data.keys())[start:stop:step] return {k: self._data[k] for k in keys} else: return self._data[key] def __setitem__(self, key, value): print(f"设置键: {key} = {value}") if isinstance(key, tuple): # 批量设置 for k, v in zip(key, value): self._data[k] = v else: self._data[key] = value def __delitem__(self, key): print(f"删除键: {key}") del self._data[key] def __len__(self): return len(self._data) def __contains__(self, key): return key in self._data# 使用d = MultiKeyDict()# 普通键d['name'] = 'Alice'd[42] = 'answer'd[3.14] = 'pi'print(d['name'])print(d[42])# 元组键d[('x', 'y')] = (10, 20)print(d[('x', 'y')])# 切片键d['a'] = 1d['b'] = 2d['c'] = 3d['d'] = 4print(d['b':'d']) # 切片访问
2. 二维容器实现
class Grid: """二维网格""" def __init__(self, rows, cols, default=None): self.rows = rows self.cols = cols self._grid = [[default for _ in range(cols)] for _ in range(rows)] def __getitem__(self, key): """支持 grid[row, col] 语法""" if isinstance(key, tuple): row, col = key return self._grid[row][col] return self._grid[key] # 返回整行 def __setitem__(self, key, value): """支持 grid[row, col] = value 语法""" if isinstance(key, tuple): row, col = key self._grid[row][col] = value else: self._grid[key] = value def __len__(self): """返回行数""" return self.rows def __repr__(self): return '\n'.join([' '.join(map(str, row)) for row in self._grid])# 使用grid = Grid(3, 3, 0)# 设置值grid[0, 0] = 1grid[1, 1] = 5grid[2, 2] = 9# 获取值print(f"grid[1,1] = {grid[1, 1]}")# 获取整行print("\n第二行:", grid[1])print("\n完整网格:")print(grid)
3. 边界检查和验证
class SafeList: """安全的列表,带边界检查""" def __init__(self, size, default=None): self.size = size self._data = [default] * size def _validate_index(self, index): """验证索引是否有效""" if not isinstance(index, int): raise TypeError("索引必须是整数") if index < 0 or index >= self.size: raise IndexError(f"索引 {index} 超出范围 [0, {self.size-1}]") def __getitem__(self, index): self._validate_index(index) return self._data[index] def __setitem__(self, index, value): self._validate_index(index) self._data[index] = value def __len__(self): return self.size def __contains__(self, item): return item in self._data# 使用safe = SafeList(5)safe[0] = 10safe[4] = 50print(f"safe[0] = {safe[0]}")try: safe[5] = 60 # 超出范围except IndexError as e: print(f"错误: {e}")try: safe["1"] = 20 # 类型错误except TypeError as e: print(f"错误: {e}")
五、__iter__ 方法详解
1. 基本迭代器实现
class Countdown: """倒计时迭代器""" def __init__(self, start): self.start = start def __iter__(self): """返回迭代器对象""" return CountdownIterator(self.start) def __reversed__(self): """反向迭代""" return CountupIterator(self.start)class CountdownIterator: def __init__(self, start): self.current = start def __iter__(self): return self def __next__(self): if self.current < 0: raise StopIteration value = self.current self.current -= 1 return valueclass CountupIterator: def __init__(self, start): self.current = 0 self.end = start def __iter__(self): return self def __next__(self): if self.current > self.end: raise StopIteration value = self.current self.current += 1 return value# 使用print("倒计时:")for num in Countdown(5): print(num, end=' ')print()print("正计时:")for num in reversed(Countdown(5)): print(num, end=' ')print()
2. 生成器实现(更简单)
class Fibonacci: """斐波那契数列""" def __init__(self, max_count): self.max_count = max_count def __iter__(self): """使用生成器实现迭代器""" a, b = 0, 1 count = 0 while count < self.max_count: yield a a, b = b, a + b count += 1 def __reversed__(self): """反向迭代(需要缓存所有值)""" values = list(self) return reversed(values)# 使用fib = Fibonacci(10)print("斐波那契数列:")for num in fib: print(num, end=' ')print()print("反向:")for num in reversed(fib): print(num, end=' ')print()
3. 树结构的迭代
class TreeNode: """树节点""" def __init__(self, value): self.value = value self.children = [] def add_child(self, child): self.children.append(child) def __iter__(self): """深度优先遍历""" yield self for child in self.children: yield from child def breadth_first(self): """广度优先遍历""" queue = [self] while queue: node = queue.pop(0) yield node queue.extend(node.children) def __reversed__(self): """反向深度优先""" for child in reversed(self.children): yield from reversed(child) yield self# 构建树root = TreeNode("A")b = TreeNode("B")c = TreeNode("C")d = TreeNode("D")e = TreeNode("E")root.add_child(b)root.add_child(c)b.add_child(d)b.add_child(e)print("深度优先遍历:")for node in root: print(node.value, end=' ')print()print("广度优先遍历:")for node in root.breadth_first(): print(node.value, end=' ')print()print("反向遍历:")for node in reversed(root): print(node.value, end=' ')print()
六、__contains__ 方法详解
1. 高效包含检查
class FastSet: """快速集合查找""" def __init__(self, items): self._items = list(items) self._index = {item: i for i, item in enumerate(items)} def __contains__(self, item): """O(1) 时间复杂度的包含检查""" return item in self._index def __len__(self): return len(self._items) def __getitem__(self, index): return self._items[index]# 使用fast_set = FastSet(range(10000))import time# 快速查找start = time.perf_counter()print(9999 in fast_set)print(f"快速查找时间: {time.perf_counter() - start:.6f}秒")# 对比普通列表normal_list = list(range(10000))start = time.perf_counter()print(9999 in normal_list)print(f"普通列表查找时间: {time.perf_counter() - start:.6f}秒")
2. 区间包含检查
class Range: """区间类""" def __init__(self, start, end): self.start = start self.end = end def __contains__(self, item): """检查是否在区间内""" return self.start <= item <= self.end def __getitem__(self, index): """支持索引访问""" if index < 0 or index > self.end - self.start: raise IndexError return self.start + index def __len__(self): return self.end - self.start + 1# 使用r = Range(5, 10)print(f"6 in r: {6in r}")print(f"4 in r: {4in r}")print(f"10 in r: {10in r}")for i in range(4, 12): print(f"{i}: {'在'if i in r else'不在'}区间")
3. 多条件包含
class Criteria: """多条件筛选""" def __init__(self): self.conditions = [] def add_condition(self, condition): self.conditions.append(condition) def __contains__(self, item): """检查是否满足所有条件""" return all(condition(item) for condition in self.conditions)# 使用criteria = Criteria()criteria.add_condition(lambda x: x > 0)criteria.add_condition(lambda x: x % 2 == 0)criteria.add_condition(lambda x: x < 10)numbers = [-2, 2, 4, 6, 8, 10, 12]print("满足所有条件的数:")for num in numbers: if num in criteria: print(f" {num}")
七、__reversed__ 方法详解
1. 自定义反向迭代
class ReversibleList: """支持反向迭代的列表""" def __init__(self, items): self._items = list(items) def __len__(self): return len(self._items) def __getitem__(self, index): return self._items[index] def __iter__(self): return iter(self._items) def __reversed__(self): """自定义反向迭代器""" print("使用自定义反向迭代器") for i in range(len(self) - 1, -1, -1): yield self._items[i] def reverse_slice(self, start, end): """反向切片""" return [self._items[i] for i in range(end - 1, start - 1, -1)]# 使用rl = ReversibleList([1, 2, 3, 4, 5])print("正向:")for item in rl: print(item, end=' ')print()print("反向:")for item in reversed(rl): print(item, end=' ')print()print("反向切片 [1:4]:", rl.reverse_slice(1, 4))
2. 双向迭代器
class BidirectionalIterator: """支持双向移动的迭代器""" def __init__(self, data): self._data = list(data) self._index = 0 def __iter__(self): return self def __next__(self): """正向移动""" if self._index >= len(self._data): raise StopIteration value = self._data[self._index] self._index += 1 return value def __reversed__(self): """返回反向迭代器""" return BidirectionalIterator(reversed(self._data)) def previous(self): """手动向前移动""" if self._index <= 0: raise StopIteration self._index -= 1 return self._data[self._index] def current(self): """返回当前元素""" if 0 <= self._index < len(self._data): return self._data[self._index] return None# 使用bi = BidirectionalIterator([10, 20, 30, 40, 50])print("正向迭代:")for item in bi: print(item, end=' ')print()print("反向迭代:")for item in reversed(bi): print(item, end=' ')print()
八、总结
1. 容器方法速查表
| | | |
|---|
__len__ | | | |
__getitem__ | | | |
__setitem__ | | | |
__delitem__ | | | |
__iter__ | | | |
__contains__ | | | |
__reversed__ | | | |
2. 设计原则
一致性:所有方法的行为应该一致
完整性:实现完整的容器协议
效率:考虑时间复杂度和空间复杂度
友好性:提供清晰的错误信息
灵活性:支持切片和负索引
3. 常见陷阱
# 陷阱1:__len__ 返回非整数class BadLen: def __len__(self): return "5" # 错误!必须返回整数# 陷阱2:__getitem__ 不处理切片class NoSlice: def __getitem__(self, index): if isinstance(index, slice): raise TypeError("不支持切片") # 应该处理切片# 陷阱3:__contains__ 效率低class SlowContains: def __contains__(self, item): return item in list(self) # 创建整个列表,效率低# 陷阱4:修改时破坏迭代class ModifyDuringIter: def __iter__(self): for item in self._data: yield item self._data.clear() # 迭代后修改,可能引起问题
4. 最佳实践
继承抽象基类:使用 collections.abc 确保实现所有必要方法
使用生成器:__iter__ 用生成器实现更简单
缓存结果:对于昂贵的操作考虑缓存
提供视图:对于大容器,考虑返回视图而不是副本
文档完善:说明容器支持的操作和行为