Чи є стандартизований метод для заміни двох змінних в Python?


345

У Python я бачив два змінні значення, які помінялися за допомогою цього синтаксису:

left, right = right, left

Чи вважається це стандартним способом заміни двох змінних значень чи є якісь інші засоби, за допомогою яких дві змінні за умовою найбільш часто змінюються?


1
@eyquem: це просто зводиться до того, чи визначається порядок оцінки мовою для призначення кортежу / списку. Python робить, більшість старих мов цього не роблять.
smci

У Hrmm C ++ є своп (a [i], a [k]), чому ми не можемо мати щось подібне для Python.
Нілс

Відповіді:


389

Python оцінює вирази зліва направо. Зауважте, що під час оцінки завдання праворуч оцінюється перед лівою частиною.

http://docs.python.org/3/reference/expressions.html#evaluation-order

Це означає наступне для виразу a,b = b,a:

  • праворуч b,aоцінюється, тобто в пам'яті створюється кортеж з двох елементів. Два елементи є об'єктами , призначених ідентифікаторами bі a, які були існуючих до інструкції encoutered під час виконання програми
  • одразу після створення цього кортежу, жодного присвоєння цього кортежного об'єкта ще не було, але це не має значення, Python внутрішньо знає, де він
  • потім оцінюється ліва частина, тобто кортеж присвоюється лівій частині
  • оскільки ліва частина складається з двох ідентифікаторів, кортеж розпаковується для того, щоб перший ідентифікатор aбув присвоєний першому елементу кортежу (що є об'єктом, який був попередньо b перед свопом, оскільки він мав ім'я b)
    та другий ідентифікатор bприсвоюється другому елементу кортежу (це об'єкт, який раніше був свопом, оскільки його ідентифікатори були a)

Цей механізм фактично поміняв місцями об'єкти, призначені ідентифікаторам aіb

Отже, щоб відповісти на ваше запитання: Так, це стандартний спосіб поміняти два ідентифікатори на два об'єкти.
До речі, об’єкти не є змінними, вони є об'єктами.


1
Наскільки я розумію, обмін двома змінними таким чином НЕ використовує зайву пам'ять, просто пам'ять для самих 2 змінних, я прав?
Catbuilts

1
@Catbuilts Побудова кортежу займе деяку додаткову пам'ять (швидше за все, це займе 3-змінна версія свопу), але оскільки єдині речі, що обмінюються - це адреси пам'яті, це не буде багато зайвої пам'яті в абсолюті сенс (можливо, 24 зайвих байти).
Brilliand

@Brilliand: Thks. Чи є у вас документи на цю тему. Це досить цікаво, і я хотів би прочитати детальніше. Дякую.
Catbuilts

1
@Catbuilts Я не впевнений, але це може допомогти прочитати, як працюють покажчики C ++. Тоді як Python намагається автоматично робити найкращі речі для вас, C ++ фактично надає вам всі можливості робити речі всіма можливими правильними та неправильними способами, тож це хороша відправна точка для того, щоб дізнатися, які недоліки мають підхід, який використовує Python . Пам'ятайте також, що наявність "64-бітної" операційної системи означає, що для зберігання адреси пам'яті потрібно 64 біти пам'яті - це частина, звідки я отримав свій "24 байт" номер.
Brilliand

Чудове пояснення. Просто додавши, що саме тому ви можете також використовувати цей метод для перестановки будь-якої кількості "змінних", наприклад a, b, c = c, a, b.
alexlomba87


38

Я знаю три способи заміни змінних, але a, b = b, aце найпростіший. є

XOR (для цілих чисел)

x = x ^ y
y = y ^ x
x = x ^ y

Або стисло,

x ^= y
y ^= x
x ^= y

Тимчасова змінна

w = x
x = y
y = w
del w

Зміна кортежу

x, y = y, x

1
Є найпростішим і єдиним, який не затуманений.
Хорхе Лейтао

17
XOR не замінює "змінні". Він обміняється цілими змінними. (Або кілька інших типів, що належним чином реалізують оператор XOR) Крім того, оскільки згідно з відповіддю Рогальського, заміна Tuple оптимізована в інтерпретаторі, насправді нічого проти цього немає. Короткий, чіткий та швидкий.
Роулер

Проблему XOR можна уникнути за допомогою + - використання оператора, але все ж я вважаю, що найкраще це a, b = b, a code= = + + yy = xy x = xycode
ashish

22

Я б не сказав, що це стандартний спосіб заміни, оскільки це призведе до несподіваних помилок.

nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]

nums[i]буде змінено першу, а потім вплине на другу змінну nums[nums[i] - 1].


2
У вас є проблема майже в будь-якій мові програмування, тобто не безпечно використовувати swap (a, b), якщо a залежить від b або навпаки. Наприклад, заміна (а, б) може бути розширена до: var c=a, a=b, b=c. І тоді, в останньому завданні буде використано нове значення aдля оцінки адреси b.
Кай Петцке

1
nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]. Вирішив би проблему.
Білл Ченг

2
@JacksonKelley Оцінка правого боку безпечна. В nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]: Проблема полягає в тому, коли python виконує призначення лівої частини, nums[i]змінюється, що призводить до nums[nums[i] - 1]зміни несподівано. Ви спочатку можете уявити, що ви хочете nums[1],nums[2] = nums[2],nums[1], але після nums[1] = nums[2]запуску у вас більше немає nums[2] = nums[1], натомість ви отримали nums[888] = nums[1].
го

5

Не працює для багатовимірних масивів, оскільки тут використовуються посилання.

import numpy as np

# swaps
data = np.random.random(2)
print(data)
data[0], data[1] = data[1], data[0]
print(data)

# does not swap
data = np.random.random((2, 2))
print(data)
data[0], data[1] = data[1], data[0]
print(data)

Дивіться також Поміняйте фрагменти масивів Numpy


Це дійсно особливість (або помилка) бібліотеки numpy.
Кай Петцке

-1

Щоб подолати проблеми, пояснені eyquem , ви можете використовувати copyмодуль для повернення кортежу, що містить (перевернуті) копії значень, за допомогою функції:

from copy import copy

def swapper(x, y):
  return (copy(y), copy(x))

Така ж функція, як lambda:

swapper = lambda x, y: (copy(y), copy(x))

Потім призначте їх потрібним іменам, наприклад:

x, y = swapper(y, x)

ПРИМІТКА. Якщо ви хочете, ви можете імпортувати / використовувати deepcopyзамість цього copy.


яку проблему ви намагаєтеся вирішити копією?
Ханан Штейнгарт

Ті, про які йшлося в публікації ейкуема .
LogicalBranch

1
але це не визначає жодних проблем, адже він каже: «Так, це стандартний спосіб замінити два ідентифікатори»
Ханан Штейнгарт

-2

Ви можете комбінувати кортежі та заміни XOR : x, y = x ^ x ^ y, x ^ y ^ y

x, y = 10, 20

print('Before swapping: x = %s, y = %s '%(x,y))

x, y = x ^ x ^ y, x ^ y ^ y

print('After swapping: x = %s, y = %s '%(x,y))

або

x, y = 10, 20

print('Before swapping: x = %s, y = %s '%(x,y))

print('After swapping: x = %s, y = %s '%(x ^ x ^ y, x ^ y ^ y))

Використання лямбда :

x, y = 10, 20

print('Before swapping: x = %s, y = %s' % (x, y))

swapper = lambda x, y : ((x ^ x ^ y), (x ^ y ^ y))

print('After swapping: x = %s, y = %s ' % swapper(x, y))

Вихід:

Before swapping: x =  10 , y =  20
After swapping: x =  20 , y =  10
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.