Назначение изменяемого компонента кортежа в python: ошибка? особенность?
Мы знаем, что кортежи Python неизменны, хорошо. Когда я пытаюсь изменить ссылку компонента кортежа, я получаю исключение, как и ожидалось. Что не ожидается, так это то, что компонент изменяется независимо от исключения, тогда как я бы подумал, что неизменность кортежа гарантирует, что объект не будет изменяемым.
Это ошибка, функция или ПКП?
In [6]: x=([1],)
In [7]: type(x)
Out[7]: tuple
In [8]: x[0]+=[2,3]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-8-a73186f99454> in <module>()
----> 1 x[0]+=[2,3]
TypeError: 'tuple' object does not support item assignment
In [9]: x
Out[9]: ([1, 2, 3],)
2 ответа
Интересный момент.
Причина, по которой он ведет себя так:
x[0]+=[2,3]
переводит на
x[0] = x[0].__iadd__([2,3])
что означает, что сначала звонит __iadd__
, который изменяет список на месте и только затем пытается выполнить недопустимое назначение в кортеж.
(Конечно, это легко обойти (например, ответ @luispedro), но я понимаю, что ваш вопрос не о том, как обойти это.)
Это ошибка, функция или ПКП?
Тяжело сказать. Я склонен голосовать за "ошибку" из-за принципа наименьшего удивления. Можно было ожидать x[0].extend(y)
вести себя как a=x[0]; a.extend(y)
вести себя как a=x[0]; a+=y
вести себя как x[0]+=y
,
Возможное исправление (по крайней мере для встроенных типов Python) может потребовать, чтобы __setitem__(self, k, v)
следует перевести на no-op в случае self[k] is v
, (и переопределение пользовательских классов __setitem__
должен подчиняться).
Здесь еще проще:
tup = ([],[])
tup[0].append(0)
tup[1].append(1)
print tup
распечатывает
([0],[1])
Неизменность кортежа означает, что объекты, составляющие кортеж, не могут быть изменены на другие объекты. Это не значит, что вы не можете изменять их значения.
Сказав это, вы нашли очень интересный (если это слово) угловой случай, который в основном означает:
x = tup[0]
x += [2,3]
tup[0] = x
Итак, первые две строки работают как положено, затем вы получите исключение.