Python присваивает нескольким переменным одно и то же значение? список поведения

132

Я попытался использовать множественное присваивание, как показано ниже, для инициализации переменных, но меня смутило поведение, я ожидаю переназначить список значений отдельно, я имею в виду, что b [0] и c [0] равны 0, как и раньше.

a=b=c=[0,3,5]
a[0]=1
print(a)
print(b)
print(c)

Результат: [1, 3, 5] [1, 3, 5] [1, 3, 5]

Это правильно? что мне использовать для многократного назначения? чем отличается от этого?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

результат: ('f:', 3) ('e:', 4)

Marco
источник
3
Вы хотите a, bи c,на все это указывает на то же значение (в данном случае список), или вы хотите a=0, b=3и c=5. В таком случае ты хочешь a,b,c = [0,3,5]или просто a,b,c = 0,3,5.
chepner

Ответы:

271

Если вы переходите на Python с языка в каталоге C / Java / etc. семье, это может помочь вам перестать думать о ней aкак о «переменной» и начать думать о ней как о «имени».

a,, bи cне разные переменные с равными значениями; это разные имена для одного и того же значения. У переменных есть типы, идентификаторы, адреса и тому подобное.

В именах ничего этого нет. Значения делать, конечно же , и вы можете иметь много имен для того же значения.

Если вы дадите Notorious B.I.G.хот-дог * Biggie Smallsи Chris Wallaceсъедите хот-дог. Если вы измените первый элемент aна 1, первые элементы bи cравны 1.

Если вы хотите узнать, называют ли два имени один и тот же объект, используйте isоператор:

>>> a=b=c=[0,3,5]
>>> a is b
True

Затем вы спрашиваете:

чем отличается от этого?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

Здесь вы повторно связываете имя eсо значением 4. Это никак не влияет на имена dи fникоим образом.

В своей предыдущей версии вы назначали a[0], а не a. Итак, с точки зрения a[0], вы перепривязываете a[0], но с точки зрения a, вы меняете его на месте.

Вы можете использовать idфункцию, которая дает вам некоторый уникальный номер, представляющий личность объекта, чтобы точно узнать, какой объект является каким, даже если он isне может помочь:

>>> a=b=c=[0,3,5]
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261120
>>> id(b[0])
4297261120

>>> a[0] = 1
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261216
>>> id(b[0])
4297261216

Обратите внимание, что значение a[0]изменилось с 4297261120 на 4297261216 - теперь это имя для другого значения. И b[0]теперь это имя того же нового значения. Это потому, что aи bвсе еще называют один и тот же объект.


По a[0]=1сути, это вызов метода для объекта списка. (Это эквивалентно a.__setitem__(0, 1).) Итак, на самом деле это вообще ничего не связывает. Это похоже на звонок my_object.set_something(1). Конечно, вероятно, объект повторно связывает атрибут экземпляра, чтобы реализовать этот метод, но не это важно; важно то, что вы ничего не назначаете, вы просто мутируете объект. То же самое и с a[0]=1.


user570826 спросил:

Что, если у нас есть a = b = c = 10

Это точно такая же ситуация, как a = b = c = [1, 2, 3]: у вас есть три имени для одного и того же значения.

Но в этом случае значение равно an int, а ints неизменяемы. В любом случае, вы можете переназначить aна другое значение (например, a = "Now I'm a string!"), но не влияет на исходное значение, которое bи cбудет по- прежнему быть имена. Разница заключается в том, что со списком, вы можете изменить значение [1, 2, 3]в [1, 2, 3, 4]практической деятельности, например, a.append(4); поскольку это фактически изменяет значение, для которого bи cявляются именами, bтеперь будет b [1, 2, 3, 4]. Невозможно изменить значение 10на что-нибудь другое. 1010 лет навсегда, точно так же, как вампир Клаудии навсегда исполнилось 5 лет (по крайней мере, пока ее не заменила Кирстен Данст).


* Предупреждение: не давайте Notorious BIG хот-дог. Зомби из гангста-рэпа нельзя кормить после полуночи.

abarnert
источник
Что, если мы have, a = b = c = 10;попытаемся обновить значение b, это повлияет на любое другое? хотя я проверил, что их идентификаторы совпадают.?
AJ
@ user570826: 10неизменяемый - это означает, что нет возможности обновить значение, поэтому ваш вопрос не имеет смысла. Вы можете указать bдругое значение, но это не повлияет на aи c, которые по-прежнему указывают на исходное значение. Разница, которую делают списки, заключается в том, что они изменяемы - например, вы можете appendиспользовать список, или lst[0] = 3, и это обновит значение, которое будет видно во всех именах этого значения.
abarnert
72

Кашель кашель

>>> a,b,c = (1,2,3)
>>> a
1
>>> b
2
>>> c
3
>>> a,b,c = ({'test':'a'},{'test':'b'},{'test':'c'})
>>> a
{'test': 'a'}
>>> b
{'test': 'b'}
>>> c
{'test': 'c'}
>>> 
Джимми Кейн
источник
10
IMHO, это на самом деле отвечает на первый ключевой вопрос OP о том, что я должен использовать для множественных заданий, тогда как более высокий рейтинг и более церебральный ответ выше нет.
Уилл Кроксфорд
2
Или a,b,c = 1,2,3без скобок работает в Python 2 или 3, если вам действительно нужен дополнительный сантиметр читабельности.
Уилл Кроксфорд
14

Да, это ожидаемое поведение. a, b и c установлены как метки для одного и того же списка. Если вам нужно три разных списка, вам нужно назначить их индивидуально. Вы можете либо повторить явный список, либо использовать один из многочисленных способов скопировать список:

b = a[:] # this does a shallow copy, which is good enough for this case
import copy
c = copy.deepcopy(a) # this does a deep copy, which matters if the list contains mutable objects

Операторы присваивания в Python не копируют объекты - они связывают имя с объектом, и объект может иметь столько меток, сколько вы установили. При первом редактировании, изменяя a [0], вы обновляете один элемент единого списка, на который ссылаются все a, b и c. Во втором случае, изменяя e, вы переключаете e на метку для другого объекта (4 вместо 3).

Питер ДеГлоппер
источник
13

В python все является объектом, включая «простые» типы переменных (int, float и т. Д.).

Когда вы изменяете значение переменной, вы фактически меняете его указатель , и если вы сравниваете две переменные, он сравнивает их указатели . (Для ясности, указатель - это адрес в физической памяти компьютера, где хранится переменная).

В результате, когда вы изменяете значение внутренней переменной, вы меняете его значение в памяти, и это влияет на все переменные, которые указывают на этот адрес.

Для вашего примера, когда вы это делаете:

a = b =  5 

Это означает, что a и b указывают на один и тот же адрес в памяти, который содержит значение 5, но когда вы это делаете:

a = 6

Это не влияет на b, потому что теперь a указывает на другую ячейку памяти, содержащую 6, а b по-прежнему указывает на адрес памяти, содержащий 5.

Но когда вы это сделаете:

a = b = [1,2,3]

a и b, опять же, указывают на одно и то же место, но разница в том, что если вы измените одно из значений списка:

a[0] = 2

Он изменяет значение памяти, на которую указывает a, но по-прежнему указывает на тот же адрес, что и b, и, как результат, b также изменяется.

Ори Сери
источник
6
Это сильно вводит в заблуждение. Указатели, безусловно, не видны на уровне Python, и по крайней мере две из четырех основных реализаций (PyPy и Jython) не используют их даже внутри реализации.
abarnert
1
Приглашаем вас прочитать и изучить внутреннее устройство Python, и вы обнаружите, что каждая переменная в Python на самом деле является указателем.
Ори Сери
4
Нет. В одной реализации Python (CPython) каждая переменная является указателем на PyObject. Это не так в других реализациях, таких как PyPy или Jython. (На самом деле, даже не ясно, как это могло быть правдой, потому что языки, на которых написаны эти реализации, даже не имеют указателей.)
abarnert
Я думаю, что использование «указателя» в концептуальном смысле нормально (возможно, с оговоркой о том, что реализации могут отличаться), особенно если цель состоит в том, чтобы передать поведение.
Левон
@abarnert Когда люди говорят о Python, они имеют в виду CPython, а не другие редко используемые реализации. Также как когда люди говорят о салфетках Kleenex, они имеют в виду ткани для лица. Игра в семантику этих комментариев действительно не нужна. Что касается того, что он написал, является ли поведение того, что он описал, неправильным?
swade
10

Вы можете использовать, id(name)чтобы проверить, представляют ли два имени один и тот же объект:

>>> a = b = c = [0, 3, 5]
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488

Списки изменяемы; это означает, что вы можете изменить значение на месте, не создавая новый объект. Однако это зависит от того, как вы измените значение:

>>> a[0] = 1
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488
>>> print(a, b, c)
[1, 3, 5] [1, 3, 5] [1, 3, 5]

Если назначить новый список a, то его идентификатор будет меняться, так что это не повлияет bи c«s значения:

>>> a = [1, 8, 5]
>>> print(id(a), id(b), id(c))
139423880 46268488 46268488
>>> print(a, b, c)
[1, 8, 5] [1, 3, 5] [1, 3, 5]

Целые числа неизменяемы, поэтому вы не можете изменить значение без создания нового объекта:

>>> x = y = z = 1
>>> print(id(x), id(y), id(z))
507081216 507081216 507081216
>>> x = 2
>>> print(id(x), id(y), id(z))
507081248 507081216 507081216
>>> print(x, y, z)
2 1 1
jurgenreza
источник
1
idне обязательно место в памяти. Как говорится в документации , это возвращает «идентификатор… целое число… которое гарантированно будет уникальным и постоянным для этого объекта в течение его жизни». CPython использует адрес памяти как адрес id, но другие реализации Python могут не использовать. PyPy, например, этого не делает. А высказывание «две переменные указывают на одну и ту же ячейку памяти» вводит в заблуждение любого, кто понимает это в стиле Си. «Два имени для одного и того же объекта» является более точным и менее вводящим в заблуждение.
abarnert
@abarnert спасибо за разъяснения, обновил ответ.
jurgenreza
7

в вашем первом примере a = b = c = [1, 2, 3]вы действительно говорите:

 'a' is the same as 'b', is the same as 'c' and they are all [1, 2, 3]

Если вы хотите установить 'a' равным 1, 'b' равным '2' и 'c' равным 3, попробуйте следующее:

a, b, c = [1, 2, 3]

print(a)
--> 1
print(b)
--> 2
print(c)
--> 3

Надеюсь это поможет!

Ник Бернс
источник
4

Проще говоря, в первом случае вы назначаете несколько имен для list. В памяти создается только одна копия списка, и все имена относятся к этому месту. Таким образом, изменение списка с использованием любого из имен фактически изменит список в памяти.

Во втором случае в памяти создается несколько копий одного и того же значения. Таким образом, каждая копия не зависит друг от друга.

Викас
источник
3

Что вам нужно, так это:

a, b, c = [0,3,5] # Unpack the list, now a, b, and c are ints
a = 1             # `a` did equal 0, not [0,3,5]
print(a)
print(b)
print(c)
pydsigner
источник
3

Код, который делает то, что мне нужно, может быть таким:

# test

aux=[[0 for n in range(3)] for i in range(4)]
print('aux:',aux)

# initialization

a,b,c,d=[[0 for n in range(3)] for i in range(4)]

# changing values

a[0]=1
d[2]=5
print('a:',a)
print('b:',b)
print('c:',c)
print('d:',d)

Результат:

('aux:', [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]])
('a:', [1, 0, 0])
('b:', [0, 0, 0])
('c:', [0, 0, 0])
('d:', [0, 0, 5])
Marco
источник