Python에서는 단순한 반복문뿐만 아니라
반복을 더 효율적으로 처리할 수 있는 여러 기능들을 제공한다.
이번 글에서는 enumerate(), zip(), iterable, iterator 그리고 comprehension까지
반복문과 함께 자주 사용되는 핵심 개념들을 함께 정리해 볼 예정이다.
1. enumerate() 함수
enumerate()는 반복문을 사용할 때
“값 + 인덱스”를 같이 쓰고 싶을 때 사용하는 내장함수다.
주로 for문과 같이 사용하는 경우가 많고,
지금 몇 번째 데이터를 처리하고 있는지 알고 싶을 때 굉장히 유용하다.
enumerate(iterable, [start=0])
- iterable : 리스트, 튜플, 문자열 등 반복 가능한 객체
- start : 인덱스 시작 번호 (기본값은 0이며, 원하는 숫자로 변경 가능)
enumerate() 기본 사용 방법
fruits = ['사과', '바나나', '딸기']
for index, fruit in enumerate(fruits):
print(f"{index}번 과일: {fruit}")
# 출력 결과:
# 0번 과일: 사과
# 1번 과일: 바나나
# 2번 과일: 딸기
시작 인덱스 변경하기
필요하면 인덱스를 1부터 시작하게 만들 수도 있다.
이럴 때 start 옵션을 사용한다.
students = ['김사과', '반하나', '오렌지']
for rank, name in enumerate(students, start=1):
print(f"{rank}등: {name}")
# 출력 결과 :
# 1등: 김사과
# 2등: 반하나
# 3등: 오렌지
💡 enumerate() 왜 사용할까?
# 함수 사용 전 코드 예시
list1 = [10, 20, 30, 40]
for i in range(len(list1)):
print(f'인덱스:{i}, 값:{list1[i]}')
# 출력결과 :
# 인덱스:0, 값:10
# 인덱스:1, 값:20
# 인덱스:2, 값:30
# 인덱스:3, 값:40
enumerate 없이도 충분히 구현은 가능하다.
하지만 직접 인덱스를 관리해야 해서 코드가 조금 번거로워진다.
위 코드처럼 len()으로 길이를 구하고,
인덱스로 값을 하나씩 꺼내는 방식은 구조가 복잡해진다.
이럴 때 enumerate()를 사용하면 인덱스 관리 없이
인덱스와 값을 동시에 받을 수 있어서 훨씬 깔끔해진다.
list1 = [10, 20, 30, 40]
for i, v in enumerate(list1): # 튜플 형태로 반환된 값을 언패킹
print(f'인덱스:{i}, 값:{v}')
# 출력결과 :
# 인덱스:0, 값:10
# 인덱스:1, 값:20
# 인덱스:2, 값:30
# 인덱스:3, 값:40
numerate()를 사용하면 좋은점은 단순하다.
- 코드가 간결해짐
- 가독성이 좋아짐
- 인덱스 관련 실수를 줄일 수 있음
2. zip() 함수
zip() 함수는 입력된 여러개의 반복 가능한(iterable) 객체로부터
하나의 요소씩 가져와 튜플형태로 묶어 반환하는 함수이다.
즉, 혼자 반복하는것이 아니라
여러개의 데이터를 동시에 반복한다.
예를 들어 두 개의 리스트가 있으면
첫 번째끼리, 두 번째끼리 이런 식으로 쌍을 만들어준다.
zip(iterable1, iterable2, ...)
zip() 기본 사용
zip()은 같은 위치의 데이터를 묶어서 반환하며,
반복문에서는 언패킹(Unpacking)으로 각각 나누어 사용할 수 있다.
하나의 변수로 받을 경우 튜플 형태로 출력된다.
li1 = [10, 20, 30]
li2 = ['apple', 'banana', 'orange']
# 언패킹 방식
for l1, l2 in zip(li1, li2):
print(l1, l2)
# 출력결과 :
# 10 apple
# 20 banana
# 30 orange
위의 코드는 튜플 언패킹을 진행한 코드이며
아래 코드는 튜플 패킹형태로 진행한 코드이다.
li1 = [10, 20, 30]
li2 = ['apple', 'banana', 'orange']
# 패킹 형태 확인
for li in zip(li1, li2):
print(li)
# 출력결과 :
# (10, 'apple')
# (20, 'banana')
# (30, 'orange')
💡 zip() 왜 사용할까?
보통은 range()나 len()을 사용하여 반복하는 경우가 많다.
하지만 해당 방식으로 사용 할 경우 아래의 문제점들이 발생할 수 있다.
- 인덱스를 직접 관리해야하는 불편함
- 리스트 길이에 의존적임
- 길이가 다를 경우 IndexError가 발생 할 수 있음
이러한 문제점을 개선한것이 바로 zip() 함수 이다.
- 인덱스를 직접 관리 할 필요 없음
- 여러 데이터를 동시에 처리 가능
- 코드가 더 간결하고 직관적임
# 좋지않은 코드 range()
# 데이터 수가 바뀔 때 프로그램이 멈추거나 IndexError가 발생할 수 있음.
li1 = [10, 20, 30]
li2 = ['apple', 'banana', 'orange']
for i in range(0, 3):
print(li1[i], li2[i])
# 좋지않은 코드 len() 함수
# li1의 길이를 구하고 해당 길이수만큼 range()함수를 활용해서 반복해줌
# But
# li1과 li2 길이가 서로 다를 수 있는데 li1에 의존하고 있는 형태임.
# 예시 ) li1이 4개이고 li2가 2개이면 IndexError 발생
li1 = [10, 20, 30]
li2 = ['apple', 'banana', 'orange']
for i in range(len(li1)):
print((li1[i], li2[i]))
zip() 주의사항
zip은 가장 짧은 리스트 기준으로 동작한다.
그래서 하나라도 짧으면 그 길이까지만 반복된다.
이 점은 꼭 기억해야 한다.
li1 = [10, 20, 30]
li2 = ['apple', 'banana']
for li in zip(li1, li2):
print(li)
# 출력결과 :
# (10, 'apple')
# (20, 'banana')
위 코드에서
- li1 → 3개
- li2 → 2개
이므로 더 짧은 li2를 기준으로 반복이 종료된다.
3. 이터러블과 이터레이터
Python에서 반복문을 사용할 때 자주 등장하는 개념이
이터러블(iterable)과 이터레이터(iterator)이다.
처음에는 이름이 비슷해서 헷갈릴 수 있지만,
반복문 내부 동작 원리를 이해할 때 중요한 개념이다.
3_1) 이터러블(iterable)
이터러블(iterable)은 반복 가능한 객체를 의미한다.
for문에서 사용할 수 있는 모든 대상이 여기에 해당된다.
대표적으로는 리스트, 문자열, 튜플 등이 있다.
즉, “for문에 넣을 수 있냐 없냐”로 생각해면 쉽다.
numbers = [10, 20, 30]
for n in numbers:
print(n)
리스트(numbers)는 반복이 가능한 객체이므로
for문에서 사용할 수 있는 이터러블 객체이다.
3_2) 이터레이터(Iterator)
이터레이터(iterator)는 데이터를 내부적으로 순서대로 관리하며,
next()를 사용해 값을 하나씩 꺼낼 수 있는 객체이다.
iter() 함수를 사용하면
iterable 객체를 iterator로 바꿀 수 있다.
numbers = [10, 20, 30]
# 이터러블 → 이터레이터로 변환
iterator = iter(numbers)
# next()로 값을 하나씩 꺼냄
print(next(iterator))
print(next(iterator))
print(next(iterator))
# print(next(iterator)) # 여기서 더 꺼내면 StopIteration 예외 발생
# 출력결과 :
# 10
# 20
# 30
위 코드 예제에서는 아래 순서대로 반환된다.
- 첫 번째 next() → 10
- 두 번째 next() → 20
- 세 번째 next() → 30
모든 데이터를 다 꺼낸 뒤 다시 next()를 호출하면
StopIteration 예외가 발생한다.
for문과의 관계
for문은 사실 내부적으로 아래 과정을 자동으로 처리한다.
- 이터레이터(iterator) 생성
- next() 반복 실행
- 끝나면 자동 종료
그래서 우리가 for문을 쓸 때는 이런 과정을 신경 쓰지 않아도 된다.
💡 이터러블과 이터레이터 차이
- 이터러블(iterable) → 반복 가능한 객체
- 이터레이터(iterator) → 값을 하나씩 꺼내는 객체
즉, 리스트 자체는 이터러블(iterable)이고,
iter()로 변환된 객체가 이터레이터(iterator)이다.
for문은 내부적으로 이터레이터(iterator)를 사용하여
데이터를 하나씩 꺼내 반복한다.
4. 컴프리헨션(Comprehension)
컴프리헨션(Comprehension)은
반복문을 사용하여 컬렉션 데이터를 간단하게 생성하는 문법이다.
컴프리헨션을 사용하면
반복문을 더 짧고 간결하게 작성할 수 있으며
보통 아래 상황에서 자주 사용된다.
- 리스트 생성
- 데이터 변환
- 조건 필터링
4_1) 리스트 컴프리헨션
리스트 컴프리헨션은
기존 리스트의 데이터를 반복하거나,
조건을 적용하여 새로운 리스트를 만들 때 자주 사용된다.
# 기본문법
[표현식 for 변수 in iterable]
# 조건문 함께 사용
[표현식 for 변수 in iterable if 조건]
기본 사용 방법 :
range(5)의 값을 반복하면서
리스트로 저장하는 구조이다.
result = [i for i in range(5)]
print(result)
# 출력결과 : [0, 1, 2, 3, 4]
조건문과 함께 사용 :
리스트 컴프리헨션에서는 if문의 위치에 따라
동작 방식이 달라진다.
① if문이 우측에 있는 경우 :
if문이 우측에 있을 경우,
조건을 만족하는 데이터만 반환한다.
즉, 조건을 만족하는 데이터만 리스트에 저장된다.
# 예제3(if문이 우측)
result = [n for n in range(10) if n % 2 == 0]
# 컴프리헨션의 if문이 우측에 있을 경우 :
# 1. 반복문 : for n in range(10)
# 2. if문 : True가 나오면 Return, False가 나오면 Return 안함
# 3. True가 되면 n 반환
print(result)
# 출력결과 : [0, 2, 4, 6, 8]
② if문이 좌측에 있는 경우 :
if문이 좌측에 있을 경우,
삼항 연산자처럼 동작한다.
- 조건이 True → 왼쪽 값 반환
- 조건이 False → 오른쪽 값 반환
즉, 조건에 따라 다른 값을 저장하는 방식이다.
# 예제4(if문이 좌측)
# 양수는 리스트에 그대로 저장하고, 음수는 0으로 변환
li = [-1, 0, -4, 24, 5, -10, 2, 20]
result = [n if n > 0 else 0 for n in li]
# 컴프리헨션의 if문이 좌측에 있을 경우 :
# 1. 반복문 : for n in li
# 2. 조건식 : n > 0
# 3. True면 n 반환
# 4. False면 0 반환
print(result)
# 출력결과 : [0, 0, 0, 24, 5, 0, 2, 20]
💡 리스트 컴프리헨션을 왜 사용할까?
- 코드를 더 짧게 작성할 수 있음
- 가독성이 좋아짐
- 리스트 생성 과정을 간단하게 표현할 수 있음
하지만,
반복문이나 조건문이 너무 복잡해질경우
오히려 가독성이 떨어질 수 있기 때문에
복잡한 로직에선 일반 반복문을 사용하는것이 더 좋다.
4_2) 세트 컴프리헨션
세트(Set)도 컴프리헨션으로 생성할 수 있다.
세트는 중복을 허용하지 않기 때문에
중복 제거에도 자주 사용된다.
# 기존 형변환 set() 함수
li = [1, 2, 3, 4, 5, 2, 3, 4]
unique_numbers = set(li)
print(unique_numbers)
# 출력결과 : {1, 2, 3, 4, 5}
# 컴프리헨션 적용
li = [1, 2, 3, 4, 5, 2, 3, 4]
# li에서 x꺼내서 x리턴 후 중골화묶으면 set이 됨
unique_numbers = {x for x in li}
print(unique_numbers)
# 출력결과 : {1, 2, 3, 4, 5}
4_3) 딕셔너리 컴프리헨션
딕셔너리도 컴프리헨션으로 생성할 수 있다.
key와 value를 동시에 생성할 수 있다는 특징이 있다.
names = ['apple', 'banana', 'orange']
# names리스트에서 하나씩 name으로 꺼내서 Return시킴
# Return 방식 → name:len(name) → key:value
name_lengths = {name:len(name) for name in names}
print(name_lengths)
# 출력결과 : {'apple': 5, 'banana': 6, 'orange': 6}
정리
- enumerate() 함수는 인덱스와 값을 함께 가져올 때 사용한다.
- zip() 함수는 여러 데이터를 동시에 반복할 때 사용한다.
- iterable은 반복 가능한 객체를 의미한다.
- iterator는 값을 하나씩 꺼낼 수 있는 객체이다.
- comprehension은 반복문을 간결하게 표현하는 문법이다.
'Python > 기초' 카테고리의 다른 글
| 파이썬 함수 심화 (변수 범위, 클로저, 콜백, 람다) (0) | 2026.05.28 |
|---|---|
| 파이썬 함수 이해하기 (개념과 동작 방식 정리) (1) | 2026.05.27 |
| 파이썬 반복문의 기초 정리(While문, for문, range 함수, continue, break, pass) (0) | 2026.05.27 |
| 파이썬 조건문(if, elif, else, 조건부 표현식, 구조적 패턴 매칭 math case) (0) | 2026.05.26 |
| 파이썬 컬렉션 자료형 정리(리스트, 튜플, 세트, 딕셔너리) (0) | 2026.05.26 |