Python __getitem__() 方法

Python __getitem__() 语法

__getitem__() 是 Python 的一个特殊方法(又称 “魔法方法” 或 “双下划线方法”),它用于定义当使用 “[]” 对类的实例进行 “索引访问(如 obj[key])” 或 “切片操作(如 obj[start:stop:step])” 时,其行为是怎样的。

__getitem__() 可以使得自定义对象能够像序列(列表、元组、字符串)或映射(字典)一样支持 “索引访问” 或 “切片操作” 。

语法:

class MyContainer:
    def __getitem__(self, key):
        # 根据 key 返回对应的元素
        if isinstance(key, int):
            # 处理整数索引
            return self._data[key]
        elif isinstance(key, slice):
            # 处理切片对象
            return self._data[key.start:key.stop:key.step]
        else:
            # 处理其他类型的键(例如,字符串键,如果对象像字典)
            raise KeyError(f'无效的键: {key}')

说明:

__getitem__() 方法接收以下 2 个参数。

  • self(必选) :约定俗成的名称,表示实例对象。
  • key(必选) :表示用于访问元素的键。key 可以是以下几种类型:
    • 如果是 obj[index],key 将是一个整数。
    • 如果是 obj['name'],key 将是一个字符串。
    • 如果是 obj[start:stop:step],key 将是一个 slice 对象,其包含 start、stop 和 step 属性。

__getitem__() 应该返回 key 对应的元素或值。如果 key 无效,应抛出 IndexError(对于序列)或 KeyError(对于映射)。

提示:

  • 如果一个类实现了 __getitem__() 方法,那么它的实例就支持使用 “[]” 操作符进行元素访问和切片。
  • 实现 __getitem__() 是使得对象 “可迭代” 的一个步骤。因为 for 循环、in 操作符以及许多内置函数(如 list()tuple())在没有 __iter__() 方法时,会回退到使用 __getitem__() 方法来按序获取元素(从索引 0 开始)。

Python __getitem__() 摘要

属于 Python 魔法方法
使用频率
官方文档 查看
相关方法 __setitem__()__delitem__()

Python __getitem__() 示例

接下来,我们通过几个简单的例子来讲解一下 Python __getitem__() 方法是如何使用的。

示例 1:__getitem__() 实现索引访问

class MySequence:
    def __init__(self, data):
        self.data = list(data)     # 内部使用列表存储数据

    def __getitem__(self, index):
        # 通过索引访问
        return self.data[index]

    def __len__(self):
        return len(self.data)

# 创建 MySequence 实例
seq = MySequence(['apple', 'banana', 'cherry', 'date'])

# 使用索引访问元素
print(seq[0])
print(seq[2])

# 尝试访问不存在的索引
try:
    print(seq[10])
except IndexError as e:
    print(e)

运行结果如下。

apple
cherry
(报错)list index out of range

分析:

我们在 MySequence 类中实现 __getitem__() 方法,使得其实例能够像普通列表一样使用整数索引访问元素。

当 seq[0] 被调用时,__getitem__() 方法被触发,并接收 “0” 作为 index 参数,然后返回 self.data[0] 的结果。

示例 2:__getitem__() 实现切片操作

class MySequence:
    def __init__(self, items):
        self.items = list(items)

    def __getitem__(self, key):
        if isinstance(key, slice):
            return self.items[key]    # 直接将切片对象传递给内部列表
        elif isinstance(key, int):
            return self.items[key]
        else:
            raise TypeError(f'不支持的键类型: {type(key)}')

    def __len__(self):
        return len(self.items)

seq = MySequence(range(10))

# 使用切片
print(seq[2:7])
print(seq[::2])
print(seq[::-1])

# 使用索引
print(seq[3])

运行结果如下。

[2, 3, 4, 5, 6]
[0, 2, 4, 6, 8]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
3

分析:

在这个例子中,当我们使用切片语法(如 seq[2:7])时,Python 会将切片表达式转换为一个 slice 对象(如 slice(2, 7, None)),并将其作为 key 参数传递给 __getitem__() 方法。然后我们可以在 __getitem__() 内部判断 key 的类型,以便分别处理索引和切片。

示例 3:__getitem__() 实现字典风格访问

class ConfigDict:
    def __init__(self):
        self._settings = {
            'username': 'lvye',
            'password': '666',
            'loglevel': 'INFO'
        }

    # 获取配置项
    def __getitem__(self, key):
        if key in self._settings:
            return self._settings[key]
        else:
            raise KeyError(f'配置项 "{key}" 不存在')

    # 设置配置项
    def __setitem__(self, key, value):
        self._settings[key] = value

    # 获取长度
    def __len__(self):
        return len(self._settings)

config = ConfigDict()

# 字典风格访问
print(config["username"])
print(config["loglevel"])

# 尝试访问不存在的键
try:
    print(config['port'])
except KeyError as e:
    print(e)

# 结合 __setitem__() 进行设置
config['role'] = 'admin'
print(config["role"])
print(len(config))

运行结果如下。

lvye
INFO
'配置项 "port" 不存在'
admin
4

分析:

__getitem__() 方法也可以用于实现字典风格的键值访问。在这个例子中,当我们使用 config['username'] 时,__getitem__() 会被调用,并接收 'username' 作为 key。通过这种方式,我们可以创建自定义的映射类型。

此外,这里同时还实现了 __setitem__() 和 __len__(),使其行为更像一个完整的字典。

示例 4:使自定义对象可迭代

class MyIterable:
    def __init__(self, max_value):
        self.max_value = max_value
        self._data = list(range(max_value))

    def __getitem__(self, index):
        # 支持索引访问,从而使对象可迭代
        if 0 <= index < self.max_value:
            return self._data[index]
        else:
            # 抛出 IndexError,告知迭代结束
            raise IndexError('超出范围')

    def __len__(self):
        return self.max_value

# for 循环遍历
for item in MyIterable(3):
    print(item)

# 转换为列表
result = list(MyIterable(4))
print(result)

运行结果如下。

0
1
2
[0, 1, 2, 3]

分析:

虽然一般情况下可以通过实现 __iter__() 方法来使对象可迭代,但如果一个类实现了 __getitem__() 并且可以接受从 0 开始的整数索引,Python 也会尝试使用它来迭代对象,直到捕获 IndexError 异常。

在这个例子中,MyIterable 类虽然没有没有显式定义 __iter__() 方法,但通过实现 __getitem__() 方法和 __len__() 方法,也可以使得其实例能够被 for 循环和 list() 函数所处理。

上一篇: __len__()

下一篇: __setitem__()

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号