Не понимаю этот код, показывающий распаковку кортежа

Вопрос. Может ли кто-нибудь объяснить вывод? Почему z равно 2 во втором print()?

Код:

x=1
y=2
x,y,z=x,x,y
print(y,x,y,z)
z,y,z=x,y,z
print(x,y,z)

Выход:

1 1 1 2
1 1 2

person Arun Upreti    schedule 24.08.2020    source источник
comment
Потому что вы устанавливаете z в y, а затем печатаете z в print(y,x,y,z)?   -  person possum    schedule 24.08.2020
comment
После создания (второго) кортежа (1, 1, 2) он распаковывает его по порядку в z,y,z. Итак, сначала он выполняет z=1, затем y=1, а затем z=2 перезаписывает только что назначенное 1.   -  person alani    schedule 24.08.2020
comment
Это может помочь вам лучше понять, как работает обмен переменными. Это вопрос нажатия и выталкивания из очереди.   -  person Rashid 'Lee' Ibrahim    schedule 24.08.2020
comment
Что смущает в коде? Как вы думаете, почему не должно быть 2?   -  person Mark Tolonen    schedule 24.08.2020
comment
@alani, как получить второй кортеж как (1,1,2)   -  person Arun Upreti    schedule 24.08.2020
comment
@MarkTolonen Весь вывод абсурден. Пожалуйста, объясните весь вывод   -  person Arun Upreti    schedule 24.08.2020
comment
Опять же, что именно вы считаете неправильным. Говоря, что это неправильно, пожалуйста, объясните, что это не говорит нам о том, что вы думаете об этом неправильно. По сути, объясните, каким, по вашему мнению, должен быть ответ и почему, и тогда мы сможем исправить ваше мышление :)   -  person Mark Tolonen    schedule 24.08.2020


Ответы (4)


Назначение кортежа Python использует стек, поэтому предполагается, что x = 1 и y = 2:

x,y,z=x,x,y

абстрактно переводится как:

push x (value 1) on stack:    top_of_stack(TOS) -> 1
push x (value 1) on stack:    TOS -> 1, 1
push y (value 2) on stack:    TOS -> 2, 1, 1
reverse top 3 stack entries:  TOS -> 1, 1, 2
pop stack and store in x (x=1)  TOS -> 1, 2
pop stack and store in y (y=1)  TOS -> 2
pop stack and store in z (z=2)  TOS -> empty

окончательный результат: x=1, y=1, z=2, следующая строка:

z,y,z=x,y,z

помещает x,y,z (1,1,2), затем загружает z=1, затем y=1, затем z=2 (перезаписывая z=1).

Вот собственно разборка с комментариями:

>>> dis.dis('x=1;y=2;x,y,z=x,x,y;z,y,z=x,y,z')
  1           0 LOAD_CONST               0 (1)     # load constant 1
              2 STORE_NAME               0 (x)     # assign to x
              4 LOAD_CONST               1 (2)     # load constant 2
              6 STORE_NAME               1 (y)     # assign to y
              8 LOAD_NAME                0 (x)     # push x on stack (1)
             10 LOAD_NAME                0 (x)     # push x again on stack (1)
             12 LOAD_NAME                1 (y)     # push y on stack (2)
             14 ROT_THREE                          # two instructions reverse stack
             16 ROT_TWO                            # .. 2,1,1 becomes 1,1,2
             18 STORE_NAME               0 (x)     # pop x=1
             20 STORE_NAME               1 (y)     # pop y=1
             22 STORE_NAME               2 (z)     # pop z=2
             24 LOAD_NAME                0 (x)     # push x (1)
             26 LOAD_NAME                1 (y)     # push y (1)
             28 LOAD_NAME                2 (z)     # push z (2)
             30 ROT_THREE                          # reverse stack
             32 ROT_TWO                            #  2,1,1 becomes 1,1,2
             34 STORE_NAME               2 (z)     # pop z=1  <---
             36 STORE_NAME               1 (y)     # pop y=1      \
             38 STORE_NAME               2 (z)     # pop z=2 (overwriting previous)

Вы также можете думать об этом более абстрактно как:

  1. Сначала вычислите значения правой стороны.
  2. Назначьте слева направо переменные справа.

So:

x=1
y=2
x,y,z=x,x,y

Означает:

# x,y,z=1,1,2
x=1
y=1
z=2

Затем:

z,y,z=x,y,z

Означает:

# z,y,z = 1,1,2
z=1
y=1
z=2   # overwriting previous z=1
person Mark Tolonen    schedule 24.08.2020
comment
Я никогда не знал, что он использует реализацию стека. Так что тут я ошибся. - person Arun Upreti; 25.08.2020

Это потому, что в Python вы можете объявлять и присваивать несколько значений переменных в одной строке слева направо.

x=1
y=2
x,y, ->z<- =x,x, ->y<-  then again, assignments are read from left to right, y is 2 and is passed to z, therefore z is now 2.
print(y,x,y,z)
z,y,z=x,y,z
print(x,y,z)

Тот же пример, но короче:

a = 10
b = 99
a, b, c = a, b, b

Мы присваиваем:

10 to a
99 to b

Тогда соответственно a, b, c = 10, 99, 99

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

Поэтому, чтобы перевести то, что происходит, это:

x, y, z = 1, 1, 2
print(1, 1, 1, 2)

В твоем случае:

print(y,x,y,z)

Содержит значение: 1, 1, 1, 2.

Это потому, что x и y содержат 1, тогда как значение z содержит значение 2, но y было напечатано два раза, поэтому это также могло произойти.

print(z, z, z, x, y, y, z)

Это выведет: (2, 2, 2, 1, 1, 1, 2)

После этого в вашем коде

z, x, y = 1, 1, 2

  print(1, 1, 2)
person John Jones    schedule 24.08.2020

Вы можете прочитать о кортежах в разделе руководства Кортежи и последовательности. В вашем коде кортежи создаются и распаковываются.

Например, до этого второго присваивания z равно 2.

z,y,z=x,y,z

Здесь python создал кортеж (1,1,2) с правой стороны, а затем распаковал слева. Итак, z переназначается на 1, y переназначается на 1... но затем z переназначается во второй раз на 2, перезаписывая первое назначение.

Когда вы пишете x,y,z, вы создаете кортеж из объектов, на которые ссылаются эти переменные. Эти объекты теперь имеют дополнительную ссылку как из переменной, так и из кортежа. И как только этот кортеж создан, ему все равно, что произойдет с этими переменными. Он не помнит их, только то, на что они ссылались.

Сценарий с ходовыми комментариями

x=1
y=2
x,y,z=x,x,y        # create tuple (1,1,2) from the right hand side x,x,y
                   # then unpack left hand side: x=1; y=1; z=2.
print(y,x,y,z)
z,y,z=x,y,z        # create a tuple (1,1,2) from the right hand side x,y,z
                   # then unpack left hand side: z=1; y=1; z=1 (overwriting z)
print(x,y,z)
person tdelaney    schedule 24.08.2020
comment
См. комментарий под вопросом Аруна Упрети, спрашивающего, откуда взялся (1,1,2). Поскольку вы разместили ответ с ним, я оставлю вас ответить на него :-) - person alani; 24.08.2020
comment
@alani - это тоже отвечает на первый вопрос ... но я посмотрю, как его доработать. - person tdelaney; 24.08.2020
comment
Спасибо за это. - person alani; 24.08.2020

В строке 3 вы устанавливаете z в y x,y,z=x,x,y

person AriesNinja    schedule 24.08.2020
comment
Вопрос касался второго print, а не первого. Ваш ответ не объясняет z,y,z=x,y,z. - person Sebastian Simon; 26.08.2020
comment
Это связано с тем, что, поскольку вы устанавливаете z в 2 значения в кортеже, он автоматически присваивает переменной второе значение, которое является самим собой. - person AriesNinja; 26.08.2020