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)


2
Ви хочете a, bі c,на все це вказує на те ж значення (в даному випадку список), або ви хочете a=0, b=3і c=5. У такому випадку ви хочете a,b,c = [0,3,5]або просто a,b,c = 0,3,5.
Чепнер

Відповіді:


271

Якщо ви їдете на Python з мови на C / Java / тощо. родина, це може допомогти вам перестати думати про 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 хот-дог. Гангста-реп-зомбі ніколи не слід годувати після півночі.


Що робити, якщо ми 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
ІМХО, це фактично відповідає на перше ключове питання ОП, що я повинен використовувати для декількох завдань, тоді як вищевикладений і більш церебральний відповідь вище не відповідає.
Буде Кроксфорд

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 не копіюють об'єкти - вони прив'язують ім'я до об'єкта, і об’єкт може мати стільки міток, скільки ви встановили. У першому редагуванні, змінюючи [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, але a все ж вказує на ту ж адресу, що і b, і як результат, b змінюється також.


6
Це дуже вводить в оману. Покажчики, безумовно, не видно на рівні Python, і принаймні дві з чотирьох основних реалізацій (PyPy та Jython) не використовують їх навіть у межах реалізації.
abarnert

1
Ви можете прочитати та вивчити внутрішні файли python, і ви виявите, що кожна змінна в python насправді вказує.
Орі Сері

4
Ні. В одній реалізації Python (CPython) кожна змінна є вказівником на a PyObject. Це не вірно в інших реалізаціях, таких як PyPy або Jython. (Насправді навіть не зрозуміло, як це могло бути правдою, оскільки мови, на яких написано реалізацію, навіть не мають покажчиків.)
abarnert

Я вважаю, що використання "покажчика" в концептуальному сенсі нормально (можливо, з відмовою від того, що реалізація може відрізнятися), особливо якщо метою є передача поведінки.
Левон

@abarnert Коли люди кажуть Python, вони мають на увазі CPython, а не інші, що рідко використовуються. Так само, як коли кажуть Kleenex, вони мають на увазі тканини обличчя. Грати в семантичну гру в цих коментарях справді непотрібно. Щодо того, що він написав, чи неправильна поведінка того, що він описав?
заплив

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на значення:

>>> 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

1
idне обов'язково місце пам'яті. Як кажуть документи , це повертає "тотожність ... ціле число ..., яке гарантовано є унікальним та постійним для цього об'єкта протягом його життя". CPython використовує адресу пам'яті як та id, але інші реалізації Python можуть не робити. PyPy, наприклад, ні. І сказати, що "два vars вказують на одне місце пам'яті" вводить в оману кожного, хто розуміє це в C-стилі. "Два імені для одного об'єкта" є і більш точними, і менш оманливими.
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

Простіше кажучи, у першому випадку ви присвоюєте кілька імен a 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)

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])
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.