Python 변수 스왑: a, b = b, a의 평가 순서와 주의점

반응형

C처럼 임시 변수를 두는 언어를 먼저 접한 뒤 Python의 a, b = b, a를 보면, 오른쪽 a가 중간에 바뀌지 않는지 궁금해진다. 결론부터 말하면 오른쪽 표현식 전체를 먼저 평가한 뒤 왼쪽 target에 대입하므로 두 값은 안전하게 바뀐다.

a = 10
b = 20

a, b = b, a

print(a, b)  # 20 10

오른쪽을 먼저 평가하고 왼쪽에 대입한다

Python 언어 참조 문서의 의미를 순서로 풀면 다음과 같다.

  1. 오른쪽 b, a를 왼쪽에서 오른쪽으로 평가한다.
  2. 그 결과를 왼쪽 target list와 짝지어 대입한다.
  3. 왼쪽 target은 왼쪽에서 오른쪽으로 처리한다.

개념적으로는 다음 임시 튜플을 떠올릴 수 있다.

temporary = (b, a)
a, b = temporary

다만 a, b = b, a를 실행할 때 반드시 실제 tuple 객체를 하나 만든다고 단정하면 안 된다. CPython은 bytecode를 최적화할 수 있고 다른 Python 구현은 내부 방식이 다를 수 있다. 코드가 기대야 할 것은 오른쪽이 먼저 평가된다는 언어 의미이지 특정 dis 출력이 아니다.

값 자체보다 이름의 연결이 바뀐다

Python 변수는 객체를 담는 상자라기보다 객체를 가리키는 이름에 가깝다. 스왑은 두 객체의 내용을 복사하는 것이 아니라 이름의 바인딩을 바꾼다.

left = [1, 2]
right = [3, 4]

left, right = right, left

이후 left는 원래 right가 가리키던 리스트를, right는 원래 left가 가리키던 리스트를 가리킨다. 리스트 내부 요소를 서로 복제한 것은 아니다.

인덱스 target도 같은 방식으로 쓸 수 있다.

values = [10, 20, 30]
i, j = 0, 2

values[i], values[j] = values[j], values[i]

print(values)  # [30, 20, 10]

오른쪽의 두 조회가 먼저 끝나므로 같은 리스트 안의 요소도 바꿀 수 있다. i == j여도 결과는 그대로다.

target이 서로 영향을 주면 순서를 봐야 한다

오른쪽이 먼저 평가된다는 사실만 기억하고 끝내면 겹치는 target에서 놀랄 수 있다. 왼쪽 target은 왼쪽부터 실제 대입하기 때문이다.

i = 0
values = [0, 1]

i, values[i] = 1, 2

print(i)       # 1
print(values)  # [0, 2]

오른쪽 (1, 2)가 먼저 준비되지만, 왼쪽에서 i = 1이 먼저 실행된다. 다음 target은 바뀐 i를 사용해 values[1] = 2가 된다. 속성 접근, 인덱스 계산, setter처럼 부수 효과가 있는 target을 한 줄에 섞을 때도 같은 순서를 확인해야 한다.

임시 변수·산술식·XOR과 비교하기

임시 변수 방식도 틀린 코드가 아니다. 여러 언어에 그대로 옮길 수 있고 디버깅 지점을 나누기 쉽다.

temporary = a
a = b
b = temporary

Python에서는 단순한 이름이나 리스트 요소를 바꿀 때 unpacking이 짧고 의도가 분명하다. 반면 덧셈·뺄셈이나 XOR로 임시 변수 없이 바꾸는 기법은 권하지 않는다.

a = a + b
b = a - b
a = a - b

산술 방식은 지원 타입이 제한되고 다른 언어의 고정 폭 정수에서는 overflow까지 고려해야 한다. XOR은 정수에만 맞고 같은 저장 위치를 다루는 구현에서 문제가 생기기 쉽다. 메모리를 의미 있게 아낀다는 근거도 없이 읽기 어려운 코드만 남을 수 있다.

검증한 경계

작성한 예제의 의미는 다음 입력을 포함한 41,488개 경우로 확인했다.

  • None, 숫자, 문자열, tuple, list, dict의 객체 참조 교환
  • 양수·음수를 포함한 임의 정수 1,000쌍
  • 길이 2부터 49까지 모든 리스트 인덱스 쌍
  • 두 인덱스가 같은 경우

이 검증은 Python 대입의 결과를 확인한 것이다. 특정 interpreter의 객체 할당 횟수나 성능을 보장하는 benchmark는 아니다.

참고 자료

반응형
KEEP READING
카테고리 전체 보기 →

댓글