list.extend와 list.iadd의 차이
재밌는 글을 봤다. list.extend와 list.iadd는 표면적으로는 동일해 보인다.
그런데 함수 내에서 global 변수를 바꾸려고 하면 동작이 달라진다.
에러는 global 변수를 바꾸려고 했다는, 흔히 볼 수 있는 에러이다.
문제는 왜 두 개가 다르냐는 것인데,
x += something은 객체의 iadd 메서드를 호출하는 것인데, 사실 아래의 코드가 실행된다.
x = x.__iadd__(something)
여기서 assignment가 문제가 된다.
흥미로운 점은, 아래 코드는 문제 없이 잘 돈다.
여기에는 assignment가 없기 때문이다. x += something은 __iadd__를 호출하고, __iadd__는 self를 반환하므로, x += something은 x의 객체가 바뀌지 않는다. 위 코드에서는 반환값을 x에 재할당하지 않으므로 에러가 발생하지 않는다.
extend는 객체의 extend 메서드를 호출하는 것으로, extend 내부에는 assignment가 없다.
비슷한 맥락의 에러는 튜플 내의 리스트를 변경하려고 할 때에도 볼 수 있다. 아래 내용은 전문가를 위한 파이썬 내용 일부를 가져와서 변경한 것이다.
튜플은 immutable이지만 튜플의 2번째 원소의 id가 바뀐 것이 아니므로 잘 동작한다. 하지만,
동일 원소에 in-place addition을 하면 에러가 발생한다. 그런데 그 와중에 연산은 또 잘 된다. dis 모듈을 이용하면 바이트코드 레벨에서 연산 과정을 볼 수 있는데,
10번에서 INPLACE_ADD를 먼저 하고 14번에서 STORE_SUBSCR을 한다. INPLACE_ADD는 문제 없이 잘 되나, STORE에서 문제가 발생한다.
어쨌든 in-place addition인 += 또는 __iadd__()는 같은 객체이더라도 무언가 할당하는 것이므로, 튜플 입장에서는 에러를 반환한다.
동일 내용은 공식 문서로도 작성되어 있다.
혹시 잘못된 부분이 있으면 친절한 댓글을...
끗