Подтвердить что ты не робот

Какова логика для x, y = y, x для замены значений в Python?

код:

x="salil"
y="Ajay"
y,x=x,y

print x +" "+ y

Вывод:

Ajay salil

Что является логическим объяснением этого?

4b9b3361

Ответ 1

Способ, которым работает этот механизм, представляет собой комбинацию двух функций - формирования неявных кортежей и распаковки кортежей/списка.

Когда вы выполните something = x, y, то, что будет делать Python, неявно создаст tuple (своего рода непреложный список), состоящий из два элемента, х и у. Итак, следующие две строки кода в точности эквивалентны:

something = x, y
something = (x, y)

Кортеж может, конечно, содержать более двух элементов:

something = a, b, c, d, e
something = (a, b, c, d, e)

Предполагаемый прецедент этой функции заключается в том, чтобы упростить/очистить работу, например, вернуть несколько значений из функции:

def foo():
    return "hello", "world"

Вторая особенность - распаковка tuple/list. Всякий раз, когда у вас есть серия переменных с левой стороны, и любой вид списка, кортежа или другой коллекции, с другой, Python будет пытаться сопоставить каждый из элементов справа от слева:

>>> a, b, c = [11, 22, 33]
>>> print(a)
11
>>> print(b)
22
>>> print(c)
33

Если это помогает, строка a, b, c = [11, 22, 33] в основном идентична выполнению:

temp = [11, 22, 33]
a = temp[0]
b = temp[1]
c = temp[2]

Обратите внимание, что правая часть может быть буквально любой коллекцией, а не только кортежами или списками. Таким образом, действует следующий код:

>>> p, q = "az"
>>> print(p + "  " + q)
a  z
>>>
>>> s, t = {'cat': 'foo', 'dog': 'bar'}
>>> print(s + "  " + t)
cat  dog

(Хотя, поскольку словари в Python не обязаны быть в каком-либо определенном порядке, и поскольку порядок ключей можно свободно скремблировать, распаковка их, вероятно, не будет полезной, так как вы потенциально получите разные результаты каждый раз.)

Если количество переменных и количество элементов в коллекции не совпадают, Python генерирует исключение:

>>> a, b = (1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 2)

>>> a, b, c = (1, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: need more than 2 values to unpack

Таким образом, это означает, что если бы мы назвали нашу функцию foo сверху и сделали следующее:

>>> x, y = foo()

... переменная x будет равна строке "hello", а переменная y будет равна строке "world".


Итак, в конечном итоге это означает, что ваш оригинальный фрагмент кода:

x = "salil"
y = "Ajay"
y, x = x, y

... логически эквивалентно следующему:

x = "salil"
y = "Ajay"
temp = (x, y)  # evaluates to ("salil", "Ajay")
y, x = temp

... который сломан еще больше, логически эквивалентен следующему:

x = "salil"
y = "Ajay"
temp = (x, y)  # evaluates to ("salil", "Ajay")
y = temp[0]
x = temp[1]

Обратите внимание, что вы можете думать, что эти две операции выполняются отдельно. Сначала формируется и оценивается кортеж, затем кортеж распаковывается обратно в переменные. Чистый эффект заключается в том, что значения ваших двух переменных взаимозаменяемы.

(Однако, как оказалось, интерпретатор CPython (исходная и стандартная реализация Python) здесь немного оптимизирован: он оптимизирует своп и не будет распаковывать полный набор кортежей - см. комментарии ниже. Я не уверен, что другие реализации Python делают то же самое, хотя я подозреваю, что они могут. В любом случае такая оптимизация специфична для реализации и не зависит от дизайна самого языка Python.)

Ответ 2

Хорошо, посмотрим:

import dis
src = '''
x="salil"
y="Ajay"
y,x=x,y

print x +" "+ y
'''
code = compile(src, '<string>', 'exec')
dis.dis(code)

Это дает:

  2           0 LOAD_CONST               0 ('salil')
              3 STORE_NAME               0 (x)

  3           6 LOAD_CONST               1 ('Ajay')
              9 STORE_NAME               1 (y)

  4          12 LOAD_NAME                0 (x)
             15 LOAD_NAME                1 (y)
             18 ROT_TWO             
             19 STORE_NAME               1 (y)
             22 STORE_NAME               0 (x)

  6          25 LOAD_NAME                0 (x)
             28 LOAD_CONST               2 (' ')
             31 BINARY_ADD          
             32 LOAD_NAME                1 (y)
             35 BINARY_ADD          
             36 PRINT_ITEM          
             37 PRINT_NEWLINE       
             38 LOAD_CONST               3 (None)
             41 RETURN_VALUE        

Помните, что Python работает как стек. В этом случае он оптимизировал присвоение команде ROT_TWO (то есть swap). Также существует инструкция ROT_THREE. Но попробуй что-нибудь еще:

import dis
src = 'x, y, z, w = a, b, c, d'
code = compile(src, '<string>', 'exec')
dis.dis(code)

Это дает общий вид:

  1           0 LOAD_NAME                0 (a)
              3 LOAD_NAME                1 (b)
              6 LOAD_NAME                2 (c)
              9 LOAD_NAME                3 (d)
             12 BUILD_TUPLE              4
             15 UNPACK_SEQUENCE          4
             18 STORE_NAME               4 (x)
             21 STORE_NAME               5 (y)
             24 STORE_NAME               6 (z)
             27 STORE_NAME               7 (w)
             30 LOAD_CONST               0 (None)
             33 RETURN_VALUE        

Ответ 3

Это работает для подкачки, поскольку сначала оценивается правая сторона =.

Итак, справа он оценивает

'salil', 'Ajay'

а затем присваивание x и y происходит

 x, y = 'salil', 'Ajay'