Notice
Recent Posts
Recent Comments
Link
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
Archives
04-19 21:08
관리 메뉴

ulismoon

[Python3] Extended Iterable Unpacking - 어-썸한 파이썬의 맛을 보아라 본문

Development

[Python3] Extended Iterable Unpacking - 어-썸한 파이썬의 맛을 보아라

ulismoon 2017. 4. 27. 16:10

어제 회사 슬랙 개발자 채널에 이런게 올라왔다.


??? 저거 뭐지 ??? *b 뒤에 뭐가 더 있다? 그런데 된다고?


해봤다.



??? 뭐지 ??? 이게 왜 되지?


파이썬 함수 인자 전달순서 같은걸 기본으로 생각하니 저런게 된다는건 생각도 못하고 있었다.


심지어 비워도 줍니다(...)


뭐 일단 다른건 모르겠고 또 이상하고 아름다운 파이썬의 매력에 빠져보기 위해 저게 어째서 되는지 찾아보기 시작했다.



1. packing, unpacking


파이썬 공식 문서에 보면 tuple을 설명하면서 packing, unpacking에 관한 내용이 마지막에 짤막하게 나온다. 굳이 tuple 한정이 아니라 sequence 자료구조라면 모두 작동하는 것으로, 아마 알게모르게 많이들 사용하고 있을 것이다. 나같은 경우에는 함수 리턴값을 여럿 주고 그걸 또 여러 변수에서 받는 곳에서 많이 사용한다.



당연히 이 방법은 리턴하는 변수의 갯수와 이걸 받는 변수의 갯수가 동일해야 작동한다. 



함수를 호출하는 쪽에서는 두 개만 받을 것을 예상하고 함수를 돌렸는데 리턴값을 변수에 배정하다보니 한개가 남아서 어쩔줄 몰라 에러를 뱉는다. 보통 저런 경우에서 앞의 두개만 쓰고 싶다거나 하면 맨 뒤에 _를 붙여서 ttt, sss, _ = test(1, 2, 3) 식으로 사용한다. 이게 어떻게 작동하는지는 정확한 원리를 몰라도 그냥 직관적으로 아 그냥 값들이 들어있는 시퀀스를 돌면서 한개씩 나눠주겠거니 하면 이해할 수 있다. 그런데 아니 저 위에서 봤던 그건 뭐야 어떻게 작동하는거야...


그래서 몇 가지 생각을 해봤다.


    • 자바스크립트에서 변수 호이스팅을 하니까 그런식으로 변수를 우선순위대로 재배치해서(함수 인자가 그러하듯 mandatory, sequence, keyword 순으로) 나눠주도록 하고 있는지도 모르겠다. 근데 그러면 저렇게 중간의 *b에 마지막 값이 아닌 중간 값이 들어가있는게 설명이 안돼....
    • 일단 앞에서부터 다 밀어넣고 뒤에 뭐가 더 남으면 뽑아서 나눠주나? 뭔가 엄청 비효율적이고 쓸모없는 짓을 하는 것 같은데...
    • 뭔가 내가 모르는 어-썸한 방법으로 매직이 일어나고 있는걸지도 몰라. 중국인한테 외주를 맡긴다거나...(?)
잘 모르겠으니 찾아보자.



2. "Extended" Iterable Unpacking


무려 "Extended" 버전 되시겠다. PEP에 있어 찾다보니 금방 나왔다. PEP 3132 에 기록되어있다.


얘기인즉슨 많은 곳에서 first, rest로 가르기를 하는데, 보통 first, rest = seq[0], seq[1:] 가 된다.

좀 보기 좋게 first, *rest = seq 형식으로 쓸 수 있다.

그러면! 이걸 확장해서 first, *else, last = seq 도 쓸 수 있어야 되는거 아니냐능!


하는 생각으로 나온 제안이다. 아래쪽을 보면 구현체가 어떻게 바뀌어야 되는지에 대한 설명도 있는데, 내가 궁금증을 해결한 곳이 바로 여기였다.


설명을 간단하게 해보자면, UNPACK_EX 라는 이름의 instruction을 추가해주고, 이런 경우에 대해서 * 후에 몇개씩의 값 배정을 기다리는 변수가 있는지를 알려주고, 이것대로 변수를 나눠준다는 내용이다. 


파이썬 바이트코드 인터프리터는 UNPACK_EX라는 케이스를 만나면 일단 * 앞에 있는 변수들에 값을 다 할당해준다. 그리고 *에 나머지를 다 밀어넣고, argcntafter 라는 이름으로 전달돼온 * 이후의 변수 갯수만큼을 뽑아서 값을 할당해준다. 마지막으로 *의 리스트 사이즈를 재조정하면 끝.


써놓고 보니 위에서 내가 생각했던 두번째 방법이랑 똑같아보인다 뭔가. 여튼 저런 방식으로 중간에 가변 길이의 *달린 변수를 넣어서 변수 갯수를 완충시키며 꼭 필요한 값은 또 원하는대로 가져갈 수 있다.


여기에서 거의 모든 케이스를 다룬 것 같은 완전체 예제를 볼 수 있으며, 또 여기에서는 파이썬 C 구현체 코드를 보면서 작동원리를 알 수 있다. 위에서 설명한 그대로 동작한다.



P.S.

그런데 이걸 보면서 한참 신기해하다가 문득

"저걸 어디서 쓰지.."

옆에서 같이 신기해하던 직원이 그랬다.

"그러게요... 딱 떠오르는 용례가 없네요... 지금까지 packing/unpacking 하면서 모자라다고 생각해본 적이 없어서..."

Comments