Python의 itertools의 chain()으로 리스트(list) 붙이기(접합, concatenation)

Python의 매력은 그 어떤 프로그래밍 언어보다 리스트(list) 다루는 방법이 우아(elegant)한 데 있습니다. 리스트 컴프리헨션(list comprehension)은 물론이거니와, itertools, numpy, pandas 같은 라이브러리도 이 우아함에 한 몫씩 하지요.

두 개의 리스트를 붙일(접합, concatenation) 때 단순히 덧셈 기호(+)를 쓰면 된다는 사실이 정말 기쁘지 않나요? 게다가 내장(built-in) 기능으로 말이지요.

shu_a = ['관우', '장비', '조운', '마초', '황충', '위연'] # 삼국지 촉나라의 무장
shu_b = ['제갈량', '방통', '서서', '강유', '법정', '마량'] # 삼국지 촉나라의 무관

shu_tot = shu_a + shu_b
print(shu_tot)
# ['관우', '장비', '조운', '마초', '황충', '위연', '제갈량', '방통', '서서', '강유', '법정', '마량']

삼국지 촉나라의 무장과 문관이 각각의 리스트로 주어졌을 때 이를 덧셈 기호만으로 붙인 사례입니다. 덧셈에 덧셈, 덧셈에 다시 덧셈을 거듭해 많은 리스트를 계속 붙여 나갈 수 있겠지요? 그런데 이게 편하기만 할까요? 수학의 덧셈 기호는 이항 연산자입니다. Python 리스트 덧셈 또한 그렇지요. n개의 리스트를 붙이려면 덧셈 기호를 (n - 1)번이나 써 줘야 합니다. 다음처럼 말이죠.

shu_tot = shu_1 + shu_2 + shu_3 +  + shu_n

붙일 리스트 개수가 대여섯 개만 넘어서도 덧셈 기호를 쓰기 귀찮아집니다. 루프(loop)를 써서 아래처럼 할 수도 있겠지만 Python스러운 방법은 아니지요. C/C++스러운 방법 아닌가요? 우리가 원하는 건 이게 아니지요.

shu_all = [shu_1, shu_2, shu_3, , shu_n]

shu_tot = []
for i in shu_all:
	shu_tot.append(i)

Python스러운 리스트 접합 방법은 itertools에서 찾을 수 있습니다. itertoolschain() 메스드의 인자인 *args에 붙일 리스트들을 입력하여 붙여진 리스트를 얻을 수 있습니다. 연습용 데이터를 아래처럼 더 확장해 보시죠. 삼국지 위촉오의 무장과 문관 목록입니다.

shu = (['관우', '장비', '조운', '마초', '황충', '위연'], ['제갈량', '방통', '서서', '강유', '법정', '마량']) # 삼국지 촉나라의 무장과 무관
wei = (['하후돈', '장료', '서황', '우금', '악진', '장합'], ['사마의', '순욱', '순유', '곽가', '가후', '정욱']) # 삼국지 위나라의 무장과 무관
wu = (['정보', '황개', '한당', '주태', '감녕', '태사자'], ['주유', '노숙', '여몽', '육손', '제갈근', '장소']) # 삼국지 오나라의 무장과 무관

튜플(tuple) 속 리스트는 총 6개지요. 덧셈 기호로 붙일 생각을 하니 벌써부터 귀찮습니다. 굳이 한다면 다음과 같이 번거롭게 할 수 있겠지요.

tot = shu[0] + shu[1] + wei[0] + wei[1] + wu[0] + wu[1]
print(tot)
# ['관우', '장비', '조운', '마초', '황충', '위연', '제갈량', '방통', '서서', '강유', '법정', '마량', '하후돈', '장료', '서황', '우금', '악진', '장합', '사마의', '순욱', '순유', '곽가', '가후', '정욱', '정보', '황개', '한당', '주태', '감녕', '태사자', '주유', '노숙', '여몽', '육손', '제갈근', '장소']

이번에는 같은 데이터를 itertoolschain()으로 우아하게 붙여 볼까요?

from itertools import chain

tot = list(chain(*shu, *wei, *wu))

어떤가요? 별표 문법(asterisk/star syntax)으로 각 튜플을 풀어 헤치고 그것들을 chain()에 몽땅 집어 넣었더니 싹 다 붙었습니다. 간단(simple)하고 간소(compact)한 것 말고도 장점이 있습니다. 튜플에 들어 있는 리스트가 많거나 적어져도 코드에 손댈 필요가 없다는 점입니다. 확장적인(scalable) 구현이 가능해지는 것이죠.

별것 아닌 것 같아도, 때때로 수백, 수천 개의 리스트를 붙인 후 가공해야 할 일이 종종 생깁니다. 덧셈 기호를 쓰더라도 어떻게든 할 수는 있지만, 보다 Python스럽게 itertoolschain을 써 보는 것은 어떨까요?