IT in General/Python

Immutable vs mutable

Algorithmus 2023. 10. 22. 14:35

파이썬을 처음 접할때 Immutable과 mutable 객체가 있다는 것을 배운다. 그리고 그에 해당하는 것을 기억하면서, 무슨 자료형은 '바꿀 수 없다'고 기억한다. 예를 들어 string은 immutable, list는 mutable 이런 식이다. 그런데 그 효과를 정확히 알지 않으면 상당한 시간을 디버깅 하느라 낭비할 수 있다. 정리해본다.

만약에 "".join(L1) 이렇게 한다고 할 때와 L1.extend(L2) 할 때 큰 차이가 무엇일까. 전자는 새 string 객체가 리턴되지만, 후자는 None이 리턴된다. 따라서, 저 결과를 무언가에 append 하고 싶다면 전자는 그냥 저 식을 argument로 넣지만(오히려 .copy() 가 안됨), 후자는 저 식을 실행한 후의 L1을 argument 로 넣되, L1.copy() 로 넣어야 한다. 만약에 L1 자체를 붙여 넣으면 그 레퍼런스만 전달이 되어 나중에 저 값들이 돌아다니면서 바뀔 수 있다.

Python 3.10.9 (main, Feb 10 2023, 10:46:20) [Clang 14.0.0 (clang-1400.0.29.202)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> L1 = ['(','(',')',')']
>>> "".join(L1)  # 새 객체 생성
'(())'

>>> L2 = ['(',')']
>>> L1.extend(L2)  # L1을 바꾼다
>>> L1
['(', '(', ')', ')', '(', ')']

>>> res = []
>>> res.append(L1.extend(L2))  # L1이 바뀌므로 리턴은 None을 함
>>> res
[None]

>>> L1   # L1이 13행에서 바뀌었음을 확인
['(', '(', ')', ')', '(', ')', '(', ')']
>>> res.append("".join(L1))    # ""는 안바뀌므로 새 객체를 리턴. 그러므로 이 식을 인자로 넣어서 append 가능
>>> res
[None, '(())()()']
>>> res.append("".join(L1).copy())  # 어차피 str인 ""에 값을 주고 join을 하면 새 객체가 생성되므로 copy 기능이 없음
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'copy'

>>> res.append(L1)  # L1의 레퍼런스가 붙여짐
>>> res
[None, '(())()()', ['(', '(', ')', ')', '(', ')', '(', ')']]  # L1이 복사되어 들어간 것 같지만...
>>> res[2][0] = 'yay'  # res에서 L1의 특정 값을 바꾸면...
>>> L1
['yay', '(', ')', ')', '(', ')', '(', ')']  # L1이 영향을 받았다

>>> res.append(L2.copy())  # 그러나 복사해서 넣으면
>>> res
[None, '(())()()', ['yay', '(', ')', ')', '(', ')', '(', ')'], ['(', ')']]
>>> res[3][0] = 'yay'  # res에서 L2의 일부분에 해당하는 값을 바꿀 때...
>>> res  # 이렇게 바뀌어도..
[None, '(())()()', ['yay', '(', ')', ')', '(', ')', '(', ')'], ['yay', ')']]
>>> L2
['(', ')']  # 원래 L2는 그대로 남아있다
>>>

유사한 것은 L.sort() 과 sorted(L) 이다. 전자는 L 을 in-place로 정렬하는 대신(mutable) None을 리턴하지만, 후자는 L을 정렬한 결과 객체를 돌려준다(L 자체는 이때는 immutable로 취급되는 것 처럼 생각해도 좋을 것이다).

반응형

'IT in General > Python' 카테고리의 다른 글

Iterator와 Generator 사용방법  (0) 2023.03.15
any와 all, 그리고 return  (0) 2022.03.25
데코레이터와 피보나치  (0) 2022.03.03
itertools.islice, tee, groupby, product  (0) 2022.03.03
functools.reduce  (0) 2022.03.02