ZetCode

Python __getitem__ 方法

最后修改于 2025 年 4 月 8 日

这篇综合指南探讨了 Python 的 __getitem__ 方法,这是一个特殊的的方法,可以实现对象的索引和切片。我们将介绍基本用法、序列模拟、自定义容器和实际示例。

基本定义

__getitem__ 方法允许对象实现下标运算符 []。当使用方括号符号访问实例时,例如 obj[key],会调用它。

主要特性:它接受实例作为第一个参数(self),键作为第二个参数,并且应该返回相应的值,或者为无效的键引发 IndexError/KeyError

基本的 __getitem__ 实现

这是一个简单的实现,展示了 __getitem__ 如何为自定义对象启用索引行为。此示例创建一个类似序列的类。

basic_getitem.py
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__ 对于创建类似字典的对象至关重要。此示例显示了一个不区分大小写的字典实现。

custom_dict.py
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__ 中的键类型来不同地处理切片。

slice_handling.py
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__ 可以动态生成值,而不是存储它们。 此示例创建一个无限的平方序列。

virtual_sequence.py
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__ 的灵活性。

切片处理包括 startstopstep 的默认值,以使序列像内置序列一样工作。

多维索引

__getitem__ 可以处理复杂的键,例如用于多维索引的元组。 此示例实现了一个简单的矩阵类。

matrix.py
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__ 如何通过检查键类型在单个实现中支持多种访问模式。

最佳实践

资料来源

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直在撰写编程文章。到目前为止,我已经撰写了超过 1,400 篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 Python 教程