2013-04-26 2 views
1

Мы знаем, что кортежи Python неизменны, хороши. Когда я пытаюсь изменить ссылку на компонент кортежа, я получаю исключение, как и ожидалось. Что не ожидается, компонент изменяется независимо от исключения, тогда как я думал, что неизменяемость гарантирует, что объект не будет изменяться.Назначение изменчивого компонента кортежа в python: ошибка? особенность?

Это ошибка, функция или PEP?

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],) 
+0

Вы не первые! – jamylak

+0

Также обратите внимание, что для того, чтобы кортеж был хешируемым, все его компоненты должны быть хешируемыми. 'hash ((1,2,3))' отлично работает. 'hash (([], [], []))' вызовет исключение. – mgilson

ответ

1

Интересный вопрос.

Причина он ведет себя так, что

x[0]+=[2,3] 

переводит

x[0] = x[0].__iadd__([2,3]) 

что означает, что сначала вызывает __iadd__, который модифицирует список на месте, и только потом пытается выполнить незаконное назначение в кортеж.

(Конечно, это легко обойти (например ответ @ luispedro), но я понимаю ваш вопрос не о том, как обойти его.)

Является ли это ошибка, функция или PEP?

Трудно сказать. Я склонен голосовать за «ошибку» из-за принципа наименьшего удивления. Можно было бы ожидать, что 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__, должны подчиняться).

+0

Да, код эквивалентен x [0] = x [0] .__ iadd __ ([2,3]).Я все еще не вижу, как меняется исходная ссылка, несмотря на исключение. Как я могу увидеть код для списка .__ iadd __ (self, other)? – p3t3

+1

При выполнении строки, такой как 'x [0] = x [0] .__ iadd __ ([2,3]), она сначала оценивает выражение с правой стороны (' __iadd__'), которое изменяет объект списка на месте. Там нет никакого отрицания. Только тогда он выполняет задание '__setitem__', что вызывает ошибку. – shx2

+1

@ shx2 Хорошее объяснение, но голосование за «ошибку» не подходит ИМО. Это так, как это работает. – glglgl

3

Вот еще проще:

tup = ([],[]) 
tup[0].append(0) 
tup[1].append(1) 
print tup 

печатает

([0],[1]) 

Кортеж неизменность означает, что объекты, которые составляют кортеж не может быть изменен на различных объектах. Это не означает, что вы не можете изменять свои значения.

Теперь, говорит, что вы нашли очень интересный (если это слово) угол случай, который в основном переводится:

x = tup[0] 
x += [2,3] 
tup[0] = x 

Итак, первые две линии работают, как и ожидалось, то вы получаете исключение.

+0

Мое мнение состояло в том, что, несмотря на то, что исключение выбрасывается, неизменный объект меняется. Вы не ожидаете этого в моем примере. – p3t3

+2

@ p3t3 Nto неизменяемый объект изменяется, но один из его (изменяемых) элементов. Это разница. – glglgl

+0

Да, исключение генерируется при попытке изменить ссылку в неизменяемом объекте, см. Ниже. – p3t3

Смежные вопросы