[파이썬] enumerate 내장 함수로 for 루프 돌리기
Feb 1, 2021 · 4 min read



많은 프로그래밍 언어들에서 i, j, k와 같은 소위 인덱스(index) 변수를 증가시키면서 for 루프를 돌립니다. 하지만 파이썬에서는 대신 enumerate()라는 내장 함수 이러한 인덱스 변수 없이 다소 독특한 방식으로 루프를 돌리는데요.

이번 포스트에서는 이 부분에 대해서 한 번 알아보도록 하겠습니다.

for 루프

먼저 파이썬에서 for 문을 사용해서 루프릴 돌리는 가장 기본적인 방법부터 짚고 넘어가겠습니다.

파이썬에서는 for 루프는 기본적으로 for <원소> in <목록>: 형태로 작성이 되는데요. 여기서 <목록> 부분에는 리스트(list), 터플(tuple), 문자열(string), 반복자(iterator), 제너레이터(generator) 등 순회가 가능한 왠만한 모든 데이터 타입을 사용할 수 있습니다. <원소> 부분은 흔히 순회 변수(loop variable)라고 하는데, <목록> 부분에 넘긴 객체가 담고 있는 원소들이 루프가 도는 동안 하나씩 차례로 할당됩니다.

예를 들어, 3개의 글자를 담고 있는 리스트를 대상으로 루프를 돌면서 각 글자를 출력하는 코드를 for 문으로 작성해보겠습니다.

>>> for letter in ['A', 'B', 'C']:
...     print(letter)
...
A
B
C

여기서 원소 뿐만 아니라 인덱스(index)도 함께 출력하고 싶을 때는 어떻게 해야할까요? 이 부분은 다른 프로그래밍 언어를 사용하시다가 파이썬으로 넘어오신 분들이 처음에 흔히 하는 질문이기도 한데요.

이러한 분들이 흔히 아래와 같은 코드를 작성하는 것을 종종 보게 됩니다.

>>> i = 0
>>> for letter in ['A', 'B', 'C']:
...     print(i, letter)
...     i += 1
...
0 A
1 B
2 C

이 방법이 틀린 것은 아니지만, i 변수가 for 반복문이 종료된 이 후에도 네임 스페이스에 남아있기 때문에 이상적이지는 않습니다.

또 다른 방법으로는 range()len() 내장 함수를 이용하여 만든 인덱스 목록을 대상으로 루프를 돌리는 것입니다.

>>> letters = ['A', 'B', 'C']
>>> for i in range(len(letters)):
...     letter = letters[i]
...     print(i, letter)
...
0 A
1 B
2 C

이 방법은 이전 방법보다는 나아보이지만, 파이썬 커뮤니티에서는 이러한 코드를 소위 파이썬 답지(Pythonic) 않아 보인다고 합니다.

enumerate 함수

그럼 어떻게 해야 좀 더 파이썬답게 인덱스(index)와 원소를 동시에 접근하면서 루프를 돌릴 수가 있을까요? 🐍

바로 파이썬의 내장 함수인 enumerate()를 이용하면 되는데요. for 문의 in 뒷 부분을 enumerate() 함수로 한 번 감싸주기만 하면 됩니다.

>>> for entry in enumerate(['A', 'B', 'C']):
...     print(entry)
...
(0, 'A')
(1, 'B')
(2, 'C')

enumerate() 함수는 기본적으로 인덱스와 원소로 이루어진 터플(tuple)을 만들어줍니다. 따라서 인덱스와 원소를 각각 다른 변수에 할당하고 싶다면 인자 풀기(unpacking)를 해줘야 합니다.

>>> for i, letter in enumerate(['A', 'B', 'C']):
...     print(i, letter)
...
0 A
1 B
2 C

시작 인덱스 바꾸기

루프를 돌리다보면 인덱스를 0이 아니라, 1로 시작하고 싶을 때가 있습니다. 이럴 때는 enumerate() 함수를 호출할 때 start 인자에 시작하고 싶은 숫자를 넘기면 됩니다.

>>> for i, letter in enumerate(['A', 'B', 'C'], start=1):
...     print(i, letter)
...
1 A
2 B
3 C
>>> for i, letter in enumerate(['A', 'B', 'C'], start=101):
...     print(i, letter)
...
101 A
102 B
103 C

enumerate 원리

지금까지 for 문에서 enumerate() 함수를 사용하는 방법에 대해서 알아봤는데요. enumerate() 함수 자체가 어떻게 작동하는지 좀 더 살펴보겠습니다.

파이썬에서 for 문은 내부적으로 in 뒤에 오는 목록을 대상으로 계속해서 next() 함수를 호출하고 있다고 생각할 수 있습니다. 따라서, 일반 리스트를 iter() 함수에 넘겨 반복자(iterator)로 만든 후 next() 함수를 호출해보면 원소들이 차례로 얻어지는 것을 알 수 있습니다.

>>> iter_letters = iter(['A', 'B', 'C'])
>>> next(iter_letters)
'A'
>>> next(iter_letters)
'B'
>>> next(iter_letters)
'C'

이 번에는 enumerate() 함수를 호출한 결과를 대상으로 next() 함수를 계속해서 호출해보면, 인덱스와 원소의 쌍이 터플(tuple)의 형태로 차례로 얻어지는 것을 알 수 있습니다.

>>> enumerate_letters = enumerate(['A', 'B', 'C'])
>>> next(enumerate_letters)
(0, 'A')
>>> next(enumerate_letters)
(1, 'B')
>>> next(enumerate_letters)
(2, 'C')

결국, enumerate() 함수는 인자로 넘어온 목록을 기준으로 인덱스와 원소를 차례대로 접근하게 해주는 반복자(iterator) 객체를 반환해주는 함수입니다. 이 부분은 enumerate() 함수의 반환 값을 리스트로 변환해보면 좀 더 명확하게 확인할 수 있습니다.

>>> list(enumerate(['A', 'B', 'C']))
[(0, 'A'), (1, 'B'), (2, 'C')]

마치면서

이상으로 파이썬에 내장된 enumerate() 함수를 이용하여 for 루프를 돌릴 때, 인덱스와 원소를 동시에 얻는 방법에 대해서 알아보았습니다. enumerate() 함수를 잘 활용하셔서 좀 더 Pythonic한 코드를 작성하실 수 있으셨으면 좋겠습니다.






Engineering Blog  by Dale Seo