Python __setitem__() 方法

Python __setitem__() 语法

__setitem__() 是 Python 的一个特殊方法(又称 “魔法方法” 或 “双下划线方法”),它用于定义当尝试通过索引或键为对象的元素赋值时(例如 obj[key] = value),其行为是怎样的。

语法:

class MyContainer:
    def __setitem__(self, key, value):
        # 根据 key 设置对应的 value
        if isinstance(key, int):
            # 处理整数索引的赋值
            self._data[key] = value
        else:
            # 处理其他类型的键的赋值(例如,字符串键,如果对象像字典)
            self._data[key] = value

说明:

__setitem__() 方法接收以下 3 个参数。

  • self(必选) :约定俗成的名称,表示实例对象。
  • key(必选) :表示用于设置元素的键,它的类型取决于赋值方式:
    • 如果是 obj[index] = value,key 将是一个整数。
    • 如果是 obj['name'] = value,key 将是一个字符串。
    • 如果是 obj[start:stop:step] = value,key 将是一个 slice 对象。
  • value(必选) :要赋给 key 对应位置的新值。

__setitem__() 方法不返回任何值(隐式返回 None)。如果 key 无效或操作不允许,应抛出相应的异常(如 IndexError、KeyError 或 TypeError)。

提示:如果一个类实现了 __setitem__() 方法,那么它的实例就支持使用 “[]” 操作符进行元素赋值和切片赋值。

Python __setitem__() 摘要

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

Python __setitem__() 示例

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

示例 1:__setitem__() 实现索引赋值

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

    def __getitem__(self, index):
        return self._data[index]

    def __setitem__(self, index, value):
        # 实现通过索引修改元素
        if not isinstance(index, int):
            raise TypeError('索引必须是整数')
        if not (0 <= index < len(self._data)):
            raise IndexError('索引超出范围')
        self._data[index] = value

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

    def __repr__(self):
        return f'MySequence({self._data})'

# 创建实例
seq = MySequence(['red', 'green', 'blue'])
print(seq)

# 通过索引赋值
seq[1] = 'yellow'
print(seq)

# 尝试修改不存在的索引
try:
    seq[3] = 'purple'
except IndexError as e:
    print(e)

# 尝试使用非整数索引
try:
    seq['color'] = 'orange'
except TypeError as e:
    print(e)

运行结果如下。

MySequence(['red', 'green', 'blue'])
MySequence(['red', 'yellow', 'blue'])
(报错)索引超出范围
(报错)索引必须是整数

分析:

在 Sequence 类中,我们手动实现了 __setitem__() 方法。该方法用于使得其实例能够像普通列表一样使用整数索引进行赋值。

当 seq[1] = 'yellow' 被执行时,__setitem__() 方法被触发,接收 “1” 作为 index 参数,'yellow' 作为 value 参数,并将其设置到内部列表的对应位置。

示例 2:__setitem__() 实现切片赋值

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

    def __getitem__(self, key):
        return self._items[key]

    def __setitem__(self, key, value):
        # 实现通过索引或切片赋值
        if isinstance(key, slice):
            # 确保切片赋值的值是可迭代的
            self._items[key] = list(value)
        elif isinstance(key, int):
            self._items[key] = value
        else:
            raise TypeError(f'不支持的键类型: {type(key)}')

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

    def __repr__(self):
        return f'MySequence({self._items})'

# 原始数据
seq = MySequence([10, 20, 30, 40, 50, 60])
print(seq)

# 索引赋值
seq[0] = 5
print(seq)

# 切片赋值 (替换部分)
seq[1:3] = [200, 300]
print(seq)

运行结果如下。

MySequence([10, 20, 30, 40, 50, 60])
MySequence([5, 20, 30, 40, 50, 60])
MySequence([5, 200, 300, 40, 50, 60])

分析:

__setitem__() 同样可以处理切片对象,从而实现切片赋值功能。在这个例子中,当我们使用切片语法(如 seq[1:3] = [200, 300])时,Python 会将切片表达式转换为一个 slice 对象,并将其作为 key 参数传递给 __setitem__()。然后我们可以在 __setitem__() 内部通过判断 key 的类型,来分别处理 “索引赋值” 和 “切片赋值” 。

需要注意的是,当切片包含步长时,赋值列表的长度必须与切片对应的元素数量一致,否则会引发 ValueError 异常。

示例 3:__setitem__() 实现字典风格的键值设置

class Settings:
    def __init__(self):
        self._config = {}

    def __getitem__(self, key):
        return self._config.get(key, None)    # 使用 get 返回 None 而不是抛 KeyError

    def __setitem__(self, key, value):
        # 实现通过键设置配置项
        if not isinstance(key, str):
            raise TypeError('设置键必须是字符串')
        self._config[key] = value

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

    def __repr__(self):
        return f'Settings({self._config})'

app = Settings()
print(app)

# 字典风格设置键值
app['database_url'] = 'sqlite:///app.db'
app['debug_mode'] = True
print(app)

# 修改现有键的值
app['debug_mode'] = False
print(app)

运行结果如下。

Settings({})
Settings({'database_url': 'sqlite:///app.db', 'debug_mode': True})
Settings({'database_url': 'sqlite:///app.db', 'debug_mode': False})

分析:

__setitem__() 也可以用于实现字典风格的键值设置。在这个例子中,当我们使用 app_settings['database_url'] = 'sqlite:///app.db' 时,__setitem__() 会被调用,并接收 'database_url' 作为 key,'sqlite:///app.db' 作为 value。

通过上面例子这种方式,我们可以创建自定义的映射类型,支持类似字典的赋值语法。

示例 4:对不可修改的自定义对象进行赋值

class MyObject:
    def __init__(self, data):
        self._data = tuple(data)   # 内部使用元组,使其不可变

    def __getitem__(self, index):
        return self._data[index]

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

    def __repr__(self):
        return f'MyObject{self._data}'

obj = MyObject([1, 2, 3])
print(obj)

# 尝试对不可变对象进行赋值
try:
    obj[0] = 100
except TypeError as e:
    print(e)

运行结果如下。

MyObject(1, 2, 3)
'MyObject' object does not support item assignment

分析:

如果一个自定义对象在设计上是不可修改的,但我们尝试对它进行元素赋值,Python 就会抛出 TypeError 异常。

上一篇: __getitem__()

下一篇: __delitem__()

给站长反馈

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

邮箱:lvyenet@vip.qq.com

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