Python의 다중 할당 개념이 헷갈려서 정리하는 글
Python은 아래의 구문과 같이 동시에 여러 변수에 값을 할당하는 다중 할당을 지원한다.
a, b = 1, 2
이렇게 동시에 a와 b에 값을 넣을 수 있다.
그런데 만약 동시에 같은 객체에 다중할당을 사용한다면 어떻게 될까?
다음과 같은 linked list 의 node class에서 다중할당을 한다고 해보자.
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
# list1 : 1 -> 3 -> 5
# list2 : 2 -> 4 -> 6
list1, list1.next = list2, list1
주석과 같이 list1 과 list2에 값이 들어가 있다고 할때,
list1 에는 list2를 할당했으니 2 → 4- > 6 이 되고, list1.next 은 1 → 3- > 5가 들어가서 최종적으로 list1의 값은 2→ 1 → 3→ 5 가 된다.
다중할당은 이런 연산을 동시에 처리시킨다.
조금 더 헷갈리는 예제를 보자.
list1 = 1, list2 = 2 → 3 일 때
# 1.
list1, list1.next , list2 = list2, list1, list2.next
# 2.
list1, list1.next = list2, list1
list2 = list2.next
1번과 2번 방법은 각각 다른 실행 결과를 낸다.
1번은 앞에서 보았듯 list1에 2→ 3 , list1.next에 1, list2에 3이 들어가는 연산을 동시에 처리하므로 최종결과는
list1 = 2 → 1 , list2 = 3 이 된다.
그렇다면 2번은?
2번은 언뜻 보면 같은 코드처럼 보이지만 2줄로 나눠졌기 때문에 동시에 처리되지 않는다.
첫번째 줄의 결과는동일하게 list1에 2→ 1 이 할당된다.
그런데 list2는 뭔가 예상과 다르게 3이 아닌 1이 들어가게 된다.
첫번째 줄의 list1 = list2 라는 파트 때문에 두 리스트가 동일한 참조가 되기 때문이다.
이러한 결과는 파이썬이 pass-by-assignment
라는 방식을 택하고 있어서다.
이는
- 파이썬에서 parameter를 패스하는 것은 실제로는 그 객체에 대한 참조를 패스한다.
- 어떤 데이터 타입은 mutable 하고 (ex. list 등) 나머지는 그렇지 않다 (ex. int, string )
를 의미한다고 한다.
이 스택오버플로 의 답변의 예시를 사용했다.
How do I pass a variable by reference?
list 타입 - append
def try_to_change_list_contents(the_list):
print('got', the_list)
the_list.append('four')
print('changed to', the_list)
outer_list = ['one', 'two', 'three']
print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)
Output :
before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']
이 함수에서는 list를 인자로 받긴 하지만 내부에서 append를 할 뿐 리턴하지는 않는다.
그러나 함수가 실행된 후 list를 출력해보면 함수 내부에서 추가한 ‘four’가 함수 바깥에서도 존재하는 것을 볼 수 있다. 이는 list 는 mutable 하기 때문에 함수의 인자로 패스되는 outer_list
가 카피가 아닌 동일한 객체에 대한 참조이기 때문이다. 따라서 함수 내부의 the_list
는 전달받은 outer_list
의 참조이고, 함수 밖에서도 그 참조에 대한 연산의 결과가 그대로 반영된다.
list 타입 - set
def try_to_change_list_reference(the_list):
print('got', the_list)
the_list = ['and', 'we', 'can', 'not', 'lie']
print('set to', the_list)
outer_list = ['we', 'like', 'proper', 'English']
print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)
Output :
before, outer_list = ['we', 'like', 'proper', 'English']
got ['we', 'like', 'proper', 'English']
set to ['and', 'we', 'can', 'not', 'lie']
after, outer_list = ['we', 'like', 'proper', 'English']
이번엔 함수 내에서 인자로 받은 list 에 아예 새로운 list를 할당해버렸다. 그런데 함수의 매개변수인 the_list
와 함수 내에서 값을 할당받는 the_list
는 같은 값이 아니기 때문에 새로운 리스트를 가리키게 되어도 함수 밖의 outer_list
의 값은 바뀌지 않는다.
더 명확한 이해를 위해 immutable한 string 타입의 예시와 비교해보자.
String 타입
def try_to_change_string_reference(the_string):
print('got', the_string)
the_string = 'In a kingdom by the sea'
print('set to', the_string)
outer_string = 'It was many and many a year ago'
print('before, outer_string =', outer_string)
try_to_change_string_reference(outer_string)
print('after, outer_string =', outer_string)
Output :
before, outer_string = It was many and many a year ago
got It was many and many a year ago # 함수 내부에서 get한 인자
set to In a kingdom by the sea # 함수 내부에서 set
after, outer_string = It was many and many a year ago
이 함수에서는 string의 값을 바꾸지만 해당 연산이 함수 밖에서는 적용되지 않는다.
string은 immutable한 타입이기 때문에 함수 내에서 the_string
의 값을 바꿀 수 없다.
한
줄
그림 요약
+ ) 추가 자료
Programming FAQ
Contents: Programming FAQ- General Questions- Is there a source code level debugger with breakpoints, single-stepping, etc.?, Are there tools to help find bugs or perform static analysis?, How can ...
docs.python.org
'PS > Python' 카테고리의 다른 글
Python - defaultdict (1) | 2023.01.18 |
---|