Python __getitem__ 方法
最后修改于 2025 年 4 月 8 日
这篇综合指南探讨了 Python 的 __getitem__ 方法,这是一个特殊的的方法,可以实现对象的索引和切片。我们将介绍基本用法、序列模拟、自定义容器和实际示例。
基本定义
__getitem__ 方法允许对象实现下标运算符 []。当使用方括号符号访问实例时,例如 obj[key],会调用它。
主要特性:它接受实例作为第一个参数(self),键作为第二个参数,并且应该返回相应的值,或者为无效的键引发 IndexError/KeyError。
基本的 __getitem__ 实现
这是一个简单的实现,展示了 __getitem__ 如何为自定义对象启用索引行为。此示例创建一个类似序列的类。
class MySequence:
def __init__(self, data):
self.data = data
def __getitem__(self, index):
return self.data[index]
seq = MySequence([10, 20, 30, 40, 50])
print(seq[1]) # 20
print(seq[-1]) # 50
print(seq[1:4]) # [20, 30, 40]
此示例演示了基本的索引和切片。 __getitem__ 方法委托给底层列表的索引功能。
Python 自动处理负索引和切片,并将它们直接传递给 __getitem__。该方法不需要特殊的切片处理。
实现自定义字典
__getitem__ 对于创建类似字典的对象至关重要。此示例显示了一个不区分大小写的字典实现。
class CaseInsensitiveDict:
def __init__(self):
self._data = {}
def __getitem__(self, key):
return self._data[key.lower()]
def __setitem__(self, key, value):
self._data[key.lower()] = value
def __contains__(self, key):
return key.lower() in self._data
d = CaseInsensitiveDict()
d['Name'] = 'John'
print(d['NAME']) # John
print('name' in d) # True
此字典通过在存储和查找之前将键转换为小写来不区分大小写地处理键。 __getitem__ 处理查找部分。
该类还实现了 __setitem__ 和 __contains__ 以实现完整的字典行为。这种模式对于自定义映射很常见。
显式处理切片
虽然 Python 自动处理基本切片,但您可以通过检查 __getitem__ 中的键类型来不同地处理切片。
class SliceProcessor:
def __getitem__(self, key):
if isinstance(key, slice):
start = key.start if key.start is not None else 0
stop = key.stop if key.stop is not None else 10
step = key.step if key.step is not None else 1
return list(range(start, stop, step))
elif isinstance(key, int):
return key * 10
else:
raise TypeError("Invalid key type")
sp = SliceProcessor()
print(sp[5]) # 50 (integer handling)
print(sp[1:5]) # [1, 2, 3, 4] (slice handling)
print(sp[1:10:2]) # [1, 3, 5, 7, 9]
此示例以不同的方式处理整数和切片。对于切片,它生成一个范围,而整数乘以 10。
isinstance(key, slice) 检查对于区分索引和切片操作至关重要。 这允许自定义切片行为。
实现虚拟序列
__getitem__ 可以动态生成值,而不是存储它们。 此示例创建一个无限的平方序列。
class Squares:
def __getitem__(self, key):
if isinstance(key, slice):
start = key.start if key.start is not None else 0
stop = key.stop if key.stop is not None else float('inf')
step = key.step if key.step is not None else 1
return [i**2 for i in range(start, stop, step)]
elif isinstance(key, int):
return key**2
else:
raise TypeError("Invalid key type")
sq = Squares()
print(sq[5]) # 25
print(sq[1:6]) # [1, 4, 9, 16, 25]
print(sq[1:10:2]) # [1, 9, 25, 49, 81]
此序列按需生成平方数,而不存储它们。单个索引和切片都可以工作,这证明了 __getitem__ 的灵活性。
切片处理包括 start、stop 和 step 的默认值,以使序列像内置序列一样工作。
多维索引
__getitem__ 可以处理复杂的键,例如用于多维索引的元组。 此示例实现了一个简单的矩阵类。
class Matrix:
def __init__(self, rows, cols):
self.rows = rows
self.cols = cols
self.data = [[0]*cols for _ in range(rows)]
def __getitem__(self, key):
if isinstance(key, tuple) and len(key) == 2:
row, col = key
return self.data[row][col]
elif isinstance(key, int):
return self.data[key]
else:
raise TypeError("Invalid key type")
m = Matrix(3, 3)
m.data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(m[1, 2]) # 6 (row 1, column 2)
print(m[2]) # [7, 8, 9] (entire row 2)
此矩阵支持单索引(整行)和双索引(特定单元格)访问模式。 __getitem__ 中的元组解包启用了多维语法。
该示例展示了 __getitem__ 如何通过检查键类型在单个实现中支持多种访问模式。
最佳实践
- 处理预期的键类型: 检查有效的键类型,并为无效的键类型引发 TypeError
- 实现错误处理: 对于超出范围的键,引发 IndexError/KeyError
- 考虑性能: __getitem__ 会被频繁调用,因此请优化其实现
- 支持切片: 处理切片对象以实现类似序列的行为
- 记录行为: 清楚地记录支持的键类型和返回值
资料来源
作者
列出所有 Python 教程。