파이썬에서 두 변수를 바꾸는 표준화 된 방법이 있습니까?


345

파이썬 에서이 구문을 사용하여 두 개의 변수 값이 바뀌는 것을 보았습니다.

left, right = right, left

이것이 두 개의 변수 값을 교환하는 표준 방법으로 간주됩니까 아니면 두 변수가 일반적으로 가장 일반적으로 교환되는 다른 방법이 있습니까?


1
@eyquem : 그것은 단순히 평가 순서 가 튜플 /리스트 할당을위한 언어에 의해 정의 되는지 여부에 달려 있습니다. 파이썬은 대부분의 오래된 언어는 그렇지 않습니다.
smci

Hrmm C ++에는 왜 스왑 (a [i], a [k])이 있는데 왜 파이썬에 대해 이와 같은 것을 가질 수 없습니까?
Nils

답변:


389

파이썬은 왼쪽에서 오른쪽으로 식을 평가합니다. 과제를 평가하는 동안 오른쪽은 왼쪽보다 먼저 평가됩니다.

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

이는 다음과 같은 표현을 의미합니다 a,b = b,a.

  • 오른쪽 b,a이 평가됩니다. 즉, 두 요소의 튜플이 메모리에 생성됩니다. 두 요소는 식별자로 지정된 객체 b이며 및 a프로그램 실행 중에 명령이 격려되기 전에 존재했습니다.
  • 이 튜플을 만든 직후 에이 튜플 객체를 할당하지 않았지만 중요하지 않습니다. 파이썬은 내부적으로 그것이 어디에 있는지 알고 있습니다
  • 그런 다음 왼쪽이 평가됩니다. 즉 튜플이 왼쪽에 할당되어 있습니다.
  • 좌측 두 개의 식별자로 구성되는 한, 튜플은 제 1 식별자가 해당 순서 풀고 a(공식적으로 약혼 된 목적하는 튜플의 첫 번째 요소에 할당 는 이름했기 때문에 스왑 전에 b)
    및 제 2 식별자는 b(이전에 있던 오브젝트 인 튜플의 두 번째 요소에 할당 그 식별자 때문에 스왑 전에 a)

이 메커니즘은 식별자에 할당 된 객체를 효과적으로 교체 a하고b

따라서 귀하의 질문에 대답하기 위해 : 예, 두 개의 식별자를 두 개의 객체로 바꾸는 표준 방법입니다.
그건 그렇고, 객체는 변수가 아니라 객체입니다.


1
내가 이해하는 한,이 방법으로 두 변수를 바꾸는 것은 여분의 메모리를 사용하지 않고 두 변수 자체의 메모리 만 사용합니다.
Catbuilts

1
@Catbuilts 튜플을 구성하면 여분의 메모리가 필요하지만 (3 변수 버전의 스왑보다 더 많이 걸릴 수 있음) 스왑되는 것은 메모리 주소뿐이므로 절대적으로 많은 여분의 메모리가 아닙니다. 의미 (24 바이트 추가).
화려한

@Brilliand : Thks. 이 주제에 대한 문서가 있습니까? 꽤 흥미롭고 더 읽어보고 싶습니다. 감사.
Catbuilts

1
@Catbuilts 확실하지 않지만 C ++ 포인터의 작동 방식을 읽는 데 도움이 될 수 있습니다. Python이 자동으로 최선의 방법으로 작업을 수행하는 반면 C ++은 실제로 가능한 모든 옳고 그른 방법으로 작업을 수행 할 수있는 모든 옵션을 제공하므로 Python이 취하는 접근 방식의 단점을 배우기위한 좋은 출발점이됩니다 . 또한 "64 비트"운영 체제를 사용한다는 것은 메모리 주소를 저장하는 데 64 비트의 메모리가 필요하다는 것을 의미합니다. 이는 "24 바이트"번호의 일부입니다.
화려한

좋은 설명입니다. 이 방법을 추가하면이 방법을 사용하여 여러 "변수"를 다시 정렬 할 수도 있습니다 (예 :) 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
난독 화되지 않은 가장 단순하고 유일한 것입니다.
Jorge Leitao

17
XOR은 "변수"를 교체하지 않습니다. 정수 변수를 교체합니다. (또는 XOR 연산자를 올바르게 구현하는 몇 가지 다른 유형) 또한 Rogalski의 답변에 따르면 Tuple Swap은 인터프리터에서 최적화되어 있으므로 실제로는 아무것도 없습니다. 짧고 깨끗하며 빠릅니다.
Rawler

XOR 문제는 +-연산자 사용으로 피할 수 있지만 여전히 가장 좋은 느낌은 a, b = b, a codex = x + yy = xy x = xycode
ashish ash

22

예기치 않은 오류가 발생하기 때문에 스왑하는 표준 방법이라고 말하고 싶지 않습니다.

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

nums[i]먼저 수정 된 다음 두 번째 변수에 영향을줍니다 nums[nums[i] - 1].


2
거의 모든 프로그래밍 언어에서 문제가 있습니다. a가 b에 의존하거나 그 반대의 경우에는 swap (a, b)를 사용하는 것이 안전하지 않습니다. 예를 들어, 교환 (A, B)가 확장 될 수있다 : var c=a, a=b, b=c. 그리고 마지막 과제는 새로운 값을 사용하여 ab의 주소를 평가합니다.
Kai Petzke 2016 년

1
nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]. 문제를 해결할 것입니다.
Bill Cheng

2
@JacksonKelley 오른쪽의 평가는 안전합니다. 에서 nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]: 파이썬이 왼쪽 할당을 수행 할 때 문제가 변경되어 예기치 않은 변경 nums[i]이 발생 nums[nums[i] - 1]합니다. U는 처음에 원하는 U를 상상할 수 nums[1],nums[2] = nums[2],nums[1]있지만 nums[1] = nums[2]실행 후에 는 더 이상 가지고 있지 않으며 nums[2] = nums[1]대신 U를 얻습니다 nums[888] = nums[1].
guo

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 라이브러리의 특별한 기능 (또는 버그)입니다.
Kai Petzke 2016 년

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


복사로 해결하려는 문제는 무엇입니까?
Hanan Shteingart 2016 년

eyquem 's post 에서 논의 된 것들 .
LogicalBranch 12

1
그러나 아무 문제도 없다고 말하지만 사실 그는 "그렇다. 두 식별자를 교환하는 표준 방법"이라고 말했다
Hanan Shteingart

-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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.