본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 1. · 15 Views
Python 함수형 프로그래밍 완벽 가이드
Python의 함수형 프로그래밍 패러다임을 초급 개발자도 쉽게 이해할 수 있도록 설명합니다. lambda, map, filter, reduce부터 제너레이터와 이터레이터까지 실무 예제와 함께 배워봅니다.
목차
1. lambda 익명 함수
어느 날 김개발 씨가 선배의 코드를 읽다가 이상한 문법을 발견했습니다. "lambda x: x * 2"라니, 이게 대체 무슨 뜻일까요?
함수 같기도 하고 아닌 것 같기도 한 이 신비로운 문법의 정체를 파헤쳐 봅시다.
lambda는 이름 없는 작은 함수를 한 줄로 만드는 방법입니다. 마치 포스트잇에 간단한 메모를 적는 것처럼, 복잡한 함수 정의 없이 즉석에서 기능을 만들어 쓸 수 있습니다.
한 번 쓰고 버릴 간단한 연산에 특히 유용합니다.
다음 코드를 살펴봅시다.
# 일반 함수로 두 배 계산하기
def double(x):
return x * 2
# lambda로 같은 기능을 한 줄로
double_lambda = lambda x: x * 2
# 실행 결과 비교
print(double(5)) # 출력: 10
print(double_lambda(5)) # 출력: 10
# lambda의 진짜 활용: 즉석에서 사용하기
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))
print(squared) # 출력: [1, 4, 9, 16, 25]
김개발 씨는 입사 2개월 차 주니어 개발자입니다. 오늘은 선배가 작성한 데이터 처리 코드를 분석하는 임무를 맡았습니다.
코드를 읽어 내려가던 중, 한 줄이 눈에 들어왔습니다. "sorted(users, key=lambda u: u.age)"라고 적혀 있었습니다.
"lambda가 뭐지?" 김개발 씨는 고개를 갸웃거렸습니다. def로 함수를 만드는 건 배웠는데, 이건 처음 보는 문법이었습니다.
마침 옆자리의 박시니어 씨가 커피를 마시며 지나가다 멈춰 섰습니다. "아, lambda 처음 보나 봐요?" 박시니어 씨가 친절하게 설명을 시작했습니다.
"lambda는 쉽게 말해서 이름 없는 함수예요. 포스트잇에 간단한 계산식 적어두는 것처럼, 한 번 쓰고 말 간단한 함수를 만들 때 쓰는 거죠." 일반적으로 함수를 만들려면 def 키워드를 쓰고, 함수 이름을 짓고, 콜론을 쓰고, 들여쓰기 후 return문까지 작성해야 합니다.
하지만 단순히 숫자를 두 배로 만드는 것처럼 간단한 작업에 이 모든 과정이 필요할까요? 바로 이런 상황에서 lambda가 빛을 발합니다.
"lambda x: x * 2"라고 쓰면 끝입니다. lambda 뒤에 매개변수를 쓰고, 콜론 다음에 반환할 값을 적으면 됩니다.
마치 수학에서 "f(x) = x * 2"라고 쓰는 것과 비슷합니다. 그렇다면 왜 굳이 lambda를 쓸까요?
가장 큰 이유는 코드의 간결함입니다. 정렬할 때 기준을 정해주거나, 데이터를 변환할 때 일일이 함수를 정의하면 코드가 길어집니다.
lambda를 쓰면 필요한 곳에 바로 작성할 수 있어서 코드 흐름이 끊기지 않습니다. 위의 코드를 살펴보겠습니다.
먼저 일반 함수 double을 정의했습니다. def, 함수명, 매개변수, return까지 3줄이 필요합니다.
반면 lambda 버전은 단 한 줄입니다. 결과는 완전히 동일합니다.
더 실용적인 예제를 보겠습니다. numbers 리스트의 각 요소를 제곱하려면 어떻게 할까요?
map 함수와 lambda를 조합하면 한 줄로 해결됩니다. 별도의 함수를 정의하지 않아도 됩니다.
실무에서 lambda는 주로 sorted, map, filter 같은 함수와 함께 사용됩니다. 예를 들어 사용자 목록을 나이순으로 정렬하고 싶다면, sorted(users, key=lambda u: u.age)처럼 쓰면 됩니다.
정렬 기준을 즉석에서 지정하는 것입니다. 하지만 주의할 점도 있습니다.
lambda는 단일 표현식만 가능합니다. 여러 줄의 로직이 필요하면 일반 함수를 써야 합니다.
또한 너무 복잡한 lambda는 오히려 가독성을 해칩니다. "간단한 것만 lambda로"라는 원칙을 기억하세요.
박시니어 씨의 설명을 들은 김개발 씨는 이해가 되기 시작했습니다. "그러니까 일회용 간단 함수를 만드는 문법이군요!" 이제 선배의 코드가 훨씬 자연스럽게 읽혔습니다.
실전 팁
💡 - lambda는 한 줄로 끝나는 간단한 연산에만 사용하세요
- 복잡한 로직은 일반 함수로 정의하는 것이 가독성에 좋습니다
- sorted, map, filter와 조합하면 강력한 데이터 처리가 가능합니다
2. map 함수 활용
김개발 씨에게 새로운 업무가 주어졌습니다. 수천 개의 가격 데이터에 부가세 10%를 더해야 합니다.
for문을 돌려야 하나 고민하던 중, 선배가 "map 써봐"라고 한마디 던졌습니다. 대체 map이 뭐길래 이렇게 자신 있게 말하는 걸까요?
map 함수는 리스트의 모든 요소에 동일한 변환을 적용하는 함수입니다. 마치 공장의 컨베이어 벨트처럼, 들어온 재료에 같은 가공을 거쳐 새로운 제품을 만들어냅니다.
반복문 없이도 데이터 변환이 가능해집니다.
다음 코드를 살펴봅시다.
# 가격 리스트에 부가세 10% 적용하기
prices = [1000, 2000, 3000, 4000, 5000]
# for문 방식 (기존 방법)
taxed_prices_loop = []
for price in prices:
taxed_prices_loop.append(price * 1.1)
# map 방식 (함수형 방법)
taxed_prices_map = list(map(lambda p: p * 1.1, prices))
print(taxed_prices_map) # [1100.0, 2200.0, 3300.0, 4400.0, 5500.0]
# 문자열 변환 예제
names = ['kim', 'lee', 'park']
upper_names = list(map(str.upper, names))
print(upper_names) # ['KIM', 'LEE', 'PARK']
김개발 씨 앞에 놓인 엑셀 파일에는 5000개의 상품 가격이 있었습니다. 모든 가격에 부가세 10%를 더해야 합니다.
처음엔 당연히 for문을 생각했습니다. 빈 리스트를 만들고, 반복문을 돌면서 하나씩 계산해서 append하면 되니까요.
코드를 작성하려는 순간, 박시니어 씨가 지나가며 한마디 했습니다. "그거 map 쓰면 한 줄이야." 김개발 씨는 의아했습니다.
아니, 5000개나 되는 데이터를 한 줄로 처리한다고요? map 함수의 개념은 놀랍도록 단순합니다.
공장의 컨베이어 벨트를 떠올려 보세요. 원재료가 벨트를 타고 들어가면, 동일한 가공 과정을 거쳐 완제품이 나옵니다.
사과가 들어가면 깎인 사과가 나오고, 감자가 들어가면 깎인 감자가 나오는 식입니다. map 함수도 똑같이 동작합니다.
리스트의 각 요소가 컨베이어 벨트를 타고 들어가고, 지정한 함수를 거쳐 새로운 값으로 변환되어 나옵니다. "map(함수, 리스트)" 형태로 사용합니다.
위 코드를 비교해 보겠습니다. for문 방식은 4줄이 필요합니다.
빈 리스트 생성, for문 선언, 계산, append까지요. 반면 map 방식은 단 1줄입니다.
list(map(lambda p: p * 1.1, prices))로 끝납니다. map은 결과로 map 객체를 반환합니다.
이것은 이터레이터라서, 리스트로 사용하려면 list()로 감싸줘야 합니다. 이 부분을 처음에 많이 헷갈려하는데, 습관처럼 list(map(...))으로 쓰면 됩니다.
두 번째 예제는 더 간단합니다. 이름 리스트를 모두 대문자로 바꾸고 싶을 때, str.upper라는 내장 메서드를 그대로 map에 전달하면 됩니다.
lambda를 쓸 필요도 없습니다. 이미 정의된 함수가 있다면 그대로 활용할 수 있습니다.
실무에서 map은 정말 자주 사용됩니다. API 응답 데이터를 원하는 형태로 변환할 때, 데이터베이스 결과를 가공할 때, 사용자 입력을 정제할 때 등 데이터 변환이 필요한 모든 곳에서 활약합니다.
성능 면에서도 map은 장점이 있습니다. map 객체는 지연 평가를 합니다.
즉, 실제로 값이 필요한 시점에 계산을 수행합니다. 대용량 데이터를 다룰 때 메모리를 효율적으로 사용할 수 있습니다.
주의할 점이 있습니다. map에 전달하는 함수는 반드시 인자를 하나만 받아야 합니다.
두 개 이상의 인자가 필요한 함수를 쓰려면, 여러 개의 리스트를 동시에 전달하거나 lambda로 감싸야 합니다. 김개발 씨는 map을 사용해서 5000개의 가격 데이터를 단 한 줄로 처리했습니다.
코드는 짧아졌고, 의도는 더 명확해졌습니다. "가격에 1.1을 곱한다"는 의도가 한눈에 들어오니까요.
for문으로 작성했을 때보다 훨씬 읽기 쉬운 코드가 되었습니다.
실전 팁
💡 - map의 결과는 반드시 list()로 감싸서 리스트로 변환하세요
- 이미 정의된 함수가 있다면 lambda 없이 함수 이름만 전달해도 됩니다
- 대용량 데이터는 list() 없이 map 객체 그대로 순회하면 메모리를 절약할 수 있습니다
3. filter 함수 활용
이번에는 쇼핑몰 데이터에서 10000원 이상인 상품만 추출해야 합니다. 김개발 씨는 for문과 if문을 조합하려다 문득 생각했습니다.
"map처럼 필터링도 간단히 하는 방법이 있지 않을까?" 있습니다. 바로 filter 함수입니다.
filter 함수는 조건에 맞는 요소만 골라내는 함수입니다. 마치 체로 쌀을 거르듯이, 원하는 조건을 통과하는 데이터만 남깁니다.
복잡한 조건문 없이 데이터 필터링이 가능해집니다.
다음 코드를 살펴봅시다.
# 상품 가격 리스트에서 10000원 이상만 추출
prices = [5000, 12000, 3000, 25000, 8000, 15000]
# for문 방식 (기존 방법)
expensive_loop = []
for price in prices:
if price >= 10000:
expensive_loop.append(price)
# filter 방식 (함수형 방법)
expensive_filter = list(filter(lambda p: p >= 10000, prices))
print(expensive_filter) # [12000, 25000, 15000]
# 빈 문자열 제거 예제
words = ['hello', '', 'world', '', 'python']
clean_words = list(filter(None, words))
print(clean_words) # ['hello', 'world', 'python']
김개발 씨에게 또 다른 미션이 떨어졌습니다. 이번에는 전체 상품 중에서 프리미엄 상품, 즉 10000원 이상인 상품만 골라내야 합니다.
마케팅팀에서 프리미엄 상품 프로모션을 준비 중이라고 합니다. 역시 처음 떠오르는 건 for문과 if문의 조합입니다.
반복문을 돌면서 조건을 확인하고, 맞으면 새 리스트에 추가하는 방식이죠. 하지만 map을 배운 김개발 씨는 궁금해졌습니다.
"필터링도 이렇게 간단히 하는 방법이 있지 않을까?" 박시니어 씨가 대답했습니다. "filter 함수가 있어.
map이 변환이라면, filter는 선별이야." 이름부터 직관적입니다. 말 그대로 걸러내는 함수입니다.
filter의 개념을 일상에서 찾아보면, 쌀을 씻을 때 쓰는 체가 떠오릅니다. 쌀알은 체에 남고, 물과 불순물은 빠져나갑니다.
체의 구멍 크기가 바로 조건입니다. filter도 마찬가지로, 조건(함수)을 만족하는 요소만 남기고 나머지는 걸러냅니다.
사용법은 map과 거의 동일합니다. "filter(조건함수, 리스트)" 형태입니다.
조건함수는 True 또는 False를 반환해야 합니다. True를 반환하면 통과, False를 반환하면 탈락입니다.
위 코드를 보겠습니다. for문 방식은 4줄입니다.
빈 리스트, for문, if문, append까지요. filter 방식은 1줄입니다.
조건이 "10000 이상"이므로 lambda p: p >= 10000을 사용했습니다. 두 번째 예제는 재미있는 트릭입니다.
filter에 None을 전달하면, 파이썬의 진리값 평가를 활용합니다. 빈 문자열, 0, None, 빈 리스트 등은 False로 평가되어 걸러집니다.
빈 문자열을 제거할 때 유용한 패턴입니다. map과 filter는 함께 사용할 때 더 강력해집니다.
예를 들어 "10000원 이상 상품의 할인가격 목록"을 구하려면 어떻게 할까요? 먼저 filter로 10000원 이상을 고르고, 그 결과에 map으로 할인을 적용하면 됩니다.
파이프라인처럼 데이터가 흐릅니다. 실무에서 filter는 유효한 데이터만 추출할 때 많이 씁니다.
삭제되지 않은 사용자만, 활성화된 상품만, 오늘 날짜의 주문만 등 조건에 맞는 데이터를 걸러내는 모든 상황에서 활용됩니다. 주의할 점은 filter의 조건함수가 반드시 boolean 값을 반환해야 한다는 것입니다.
0이나 빈 문자열도 False로 취급되므로, 의도치 않게 유효한 데이터가 걸러질 수 있습니다. 조건을 명확히 작성하는 것이 중요합니다.
김개발 씨는 filter를 활용해서 프리미엄 상품 목록을 깔끔하게 추출했습니다. for문과 if문을 쓸 때보다 코드의 의도가 명확해졌습니다.
"이 가격들 중에서 10000 이상만 필터링한다"는 의미가 코드에서 바로 읽히니까요.
실전 팁
💡 - filter(None, 리스트)는 False로 평가되는 모든 값을 제거합니다
- 복잡한 조건은 별도 함수로 정의하면 가독성이 좋아집니다
- map과 filter를 체이닝하면 데이터 파이프라인을 구성할 수 있습니다
4. reduce 함수 활용
김개발 씨 앞에 새로운 과제가 놓였습니다. 장바구니에 담긴 모든 상품의 총 합계를 구해야 합니다.
sum() 함수가 떠올랐지만, 선배는 "reduce를 알면 더 다양한 집계가 가능해"라고 말합니다. reduce는 대체 무엇이고, 왜 필요한 걸까요?
reduce 함수는 리스트의 모든 요소를 하나의 값으로 축약하는 함수입니다. 마치 눈덩이가 굴러가며 점점 커지듯이, 앞에서부터 차례대로 값을 누적시켜 최종 결과를 만들어냅니다.
합계, 곱, 최댓값 등 집계 연산에 활용됩니다.
다음 코드를 살펴봅시다.
from functools import reduce
# 장바구니 총 합계 구하기
cart = [15000, 23000, 8000, 12000]
# for문 방식
total_loop = 0
for price in cart:
total_loop += price
# reduce 방식
total_reduce = reduce(lambda acc, price: acc + price, cart)
print(total_reduce) # 58000
# 리스트 요소 모두 곱하기
numbers = [1, 2, 3, 4, 5]
product = reduce(lambda acc, n: acc * n, numbers)
print(product) # 120 (1*2*3*4*5)
# 초기값 지정
total_with_init = reduce(lambda acc, price: acc + price, cart, 5000)
print(total_with_init) # 63000 (5000 + 58000)
"장바구니 합계를 구하려면 어떻게 해야 할까요?" 김개발 씨가 물었습니다. 당장 떠오르는 건 for문으로 더하거나, 내장 함수 sum()을 쓰는 것입니다.
하지만 박시니어 씨는 한 발 더 나아갔습니다. "합계야 sum()으로 되지만, 곱셈이나 최댓값을 누적으로 구해야 할 때는?" 김개발 씨는 멈칫했습니다.
"그때마다 for문을 새로 짜야 하나요?" 박시니어 씨가 고개를 저었습니다. "reduce를 알면 다 한 줄로 가능해." reduce는 "줄이다, 축약하다"라는 뜻입니다.
여러 개의 값을 하나의 값으로 줄이는 함수입니다. 눈덩이를 굴리는 모습을 상상해 보세요.
작은 눈덩이가 언덕을 굴러 내려가며 눈을 계속 덧붙입니다. 결국 커다란 눈덩이 하나가 됩니다.
reduce도 같은 원리입니다. 리스트의 첫 번째 요소에서 시작해서, 다음 요소와 연산하고, 그 결과를 또 다음 요소와 연산합니다.
리스트 끝까지 가면 최종 결과 하나가 남습니다. Python 3에서 reduce는 내장 함수가 아닙니다.
functools 모듈에서 import해야 합니다. "from functools import reduce"를 먼저 작성해야 합니다.
이 점을 잊어서 에러가 나는 경우가 많으니 주의하세요. 사용법을 보겠습니다.
"reduce(함수, 리스트)"입니다. 함수는 두 개의 인자를 받습니다.
첫 번째는 누적값(accumulator), 두 번째는 현재 요소입니다. 함수의 반환값이 다음 단계의 누적값이 됩니다.
위 코드에서 장바구니 합계를 구하는 과정을 따라가 봅시다. 처음에 acc는 15000(첫 요소)입니다.
두 번째 요소 23000과 더해서 38000이 됩니다. 다시 8000을 더해 46000, 12000을 더해 58000이 최종 결과입니다.
곱셈 예제도 같은 원리입니다. lambda acc, n: acc * n으로 누적 곱셈을 수행합니다.
1부터 5까지의 곱, 즉 5 팩토리얼(120)을 구할 수 있습니다. 단순한 패턴 변경으로 완전히 다른 집계가 가능해집니다.
세 번째 인자로 초기값을 지정할 수도 있습니다. reduce(함수, 리스트, 초기값) 형태입니다.
초기값을 주면 누적 계산이 그 값에서 시작합니다. 빈 리스트에 reduce를 적용할 때 초기값이 없으면 에러가 나므로, 초기값 지정이 안전한 방법입니다.
실무에서 reduce는 단순 합계보다 복잡한 집계에 유용합니다. 객체 리스트에서 특정 필드의 합을 구하거나, 중첩 리스트를 평탄화하거나, 딕셔너리를 병합할 때 등 다양하게 활용됩니다.
하지만 주의할 점이 있습니다. reduce는 강력하지만, 가독성이 떨어질 수 있습니다.
단순 합계는 sum()이, 최댓값은 max()가 더 명확합니다. reduce는 내장 함수로 해결하기 어려운 복잡한 집계에 사용하세요.
김개발 씨는 reduce의 원리를 이해하고 나니, 데이터를 다루는 시야가 넓어진 느낌이었습니다. "map으로 변환하고, filter로 선별하고, reduce로 집계하면 거의 모든 데이터 처리가 가능하겠네요!"
실전 팁
💡 - reduce는 functools에서 import해야 합니다
- 빈 리스트 대비를 위해 초기값을 지정하는 것이 안전합니다
- 단순 합계/최대/최소는 내장 함수(sum, max, min)가 더 명확합니다
5. 제너레이터 기초
어느 날 김개발 씨의 프로그램이 메모리 부족으로 터졌습니다. 100만 개의 데이터를 리스트에 담으려다 생긴 일입니다.
"메모리를 아끼면서 대용량 데이터를 다루는 방법이 없을까요?" 박시니어 씨가 답했습니다. "제너레이터를 쓰면 돼."
제너레이터는 값을 한 번에 다 만들지 않고, 필요할 때마다 하나씩 생성하는 특별한 함수입니다. 마치 빵집에서 빵을 미리 다 구워두지 않고, 손님이 올 때마다 하나씩 굽는 것과 같습니다.
메모리를 획기적으로 절약할 수 있습니다.
다음 코드를 살펴봅시다.
# 일반 함수: 모든 값을 한 번에 메모리에 올림
def get_numbers_list(n):
result = []
for i in range(n):
result.append(i * i)
return result
# 제너레이터: 값을 하나씩 생성
def get_numbers_gen(n):
for i in range(n):
yield i * i # return 대신 yield 사용
# 사용 비교
numbers_list = get_numbers_list(5) # [0, 1, 4, 9, 16] 전체가 메모리에
numbers_gen = get_numbers_gen(5) # 제너레이터 객체만 생성
# 제너레이터 순회
for num in numbers_gen:
print(num) # 0, 1, 4, 9, 16 하나씩 출력
# 제너레이터 표현식 (간단한 버전)
squares = (x * x for x in range(5)) # 소괄호 사용
그날의 사건은 김개발 씨에게 큰 교훈이 되었습니다. 100만 건의 로그 데이터를 분석하는 프로그램을 만들었는데, 실행하자마자 메모리 에러가 떴습니다.
모든 데이터를 리스트에 담으려니 메모리가 버티지 못한 것입니다. 박시니어 씨가 코드를 보더니 말했습니다.
"100만 개를 한꺼번에 메모리에 올릴 필요가 있어? 어차피 하나씩 처리할 건데?" 그 말에 김개발 씨는 깨달았습니다.
한 번에 하나씩만 쓸 건데, 굳이 전부 만들어둘 필요가 없었습니다. 제너레이터는 이런 문제를 해결합니다.
일반 함수가 빵 100개를 미리 다 구워서 진열대에 쌓아두는 것이라면, 제너레이터는 손님이 올 때마다 빵을 하나씩 굽는 것입니다. 필요한 순간에만 값을 생성하니, 메모리를 거의 쓰지 않습니다.
제너레이터를 만드는 방법은 간단합니다. 일반 함수에서 return 대신 yield를 사용하면 됩니다.
yield는 "양보하다"라는 뜻인데, 값을 하나 내보내고 잠시 멈춘다는 의미입니다. 다음에 값을 요청하면 멈췄던 곳에서 다시 시작합니다.
위 코드를 비교해 보겠습니다. get_numbers_list는 리스트를 만들어 반환합니다.
n이 100만이면 100만 개의 값이 메모리에 올라갑니다. 반면 get_numbers_gen은 제너레이터입니다.
호출해도 계산이 시작되지 않고, 제너레이터 객체만 반환됩니다. 제너레이터 객체를 for문으로 순회하면 그때서야 값이 하나씩 생성됩니다.
첫 번째 반복에서 00=0이 생성되고, 두 번째 반복에서 11=1이 생성됩니다. 이전 값은 메모리에서 사라지고 새 값만 유지됩니다.
더 간단한 방법도 있습니다. 제너레이터 표현식입니다.
리스트 컴프리헨션과 비슷하게 생겼는데, 대괄호 대신 소괄호를 씁니다. [xx for x in range(5)]는 리스트를, (xx for x in range(5))는 제너레이터를 만듭니다.
실무에서 제너레이터는 대용량 파일 처리에 많이 쓰입니다. 기가바이트짜리 로그 파일을 한 줄씩 읽으면서 처리할 때, 제너레이터 없이는 메모리가 감당이 안 됩니다.
파이썬의 파일 객체 자체도 제너레이터처럼 동작합니다. 주의할 점이 있습니다.
제너레이터는 한 번만 순회 가능합니다. for문을 한 번 돌고 나면 소진됩니다.
다시 순회하려면 제너레이터를 새로 만들어야 합니다. 또한 인덱싱이나 len()이 불가능합니다.
순차 접근만 가능하다는 것을 기억하세요. 김개발 씨는 코드를 제너레이터로 바꿨습니다.
100만 건의 데이터를 처리하는데 메모리 사용량이 거의 늘지 않았습니다. "데이터가 아무리 커도 괜찮겠네요!" 메모리 효율의 마법을 체험한 순간이었습니다.
실전 팁
💡 - 대용량 데이터 처리에는 리스트 대신 제너레이터를 사용하세요
- 제너레이터는 한 번만 순회 가능하므로 재사용이 필요하면 새로 생성해야 합니다
- 제너레이터 표현식은 소괄호()로, 리스트 컴프리헨션은 대괄호[]로 구분합니다
6. 이터레이터 프로토콜
김개발 씨가 for문에 대해 깊이 생각해 본 적이 있습니다. "for item in items"가 어떻게 동작하는 걸까요?
리스트도 되고, 문자열도 되고, 심지어 파일도 되는데요. 그 비밀은 바로 이터레이터 프로토콜에 있습니다.
이터레이터는 값을 순차적으로 꺼낼 수 있는 객체입니다. for문이 동작하는 원리가 바로 이터레이터 프로토콜입니다.
iter()로 이터레이터를 얻고, next()로 다음 값을 가져오는 것이 전부입니다. 이 원리를 알면 나만의 순회 가능한 객체를 만들 수 있습니다.
다음 코드를 살펴봅시다.
# 리스트에서 이터레이터 얻기
numbers = [10, 20, 30]
iterator = iter(numbers) # 이터레이터 생성
# next()로 값 하나씩 꺼내기
print(next(iterator)) # 10
print(next(iterator)) # 20
print(next(iterator)) # 30
# print(next(iterator)) # StopIteration 에러 발생!
# for문의 실제 동작 원리
colors = ['red', 'green', 'blue']
# 아래 두 코드는 동일하게 동작합니다
for color in colors:
print(color)
# 위 for문은 사실 이렇게 동작합니다
color_iter = iter(colors)
while True:
try:
color = next(color_iter)
print(color)
except StopIteration:
break
파이썬을 배우면서 누구나 for문을 씁니다. "for i in range(10)"처럼요.
그런데 김개발 씨는 문득 궁금해졌습니다. for문이 리스트도, 문자열도, 딕셔너리도, 심지어 파일도 순회할 수 있는 이유가 뭘까요?
박시니어 씨가 설명했습니다. "for문은 사실 이터레이터 프로토콜을 따르는 거야.
이 프로토콜을 구현한 객체라면 뭐든 for문으로 순회할 수 있어." 프로토콜이란 일종의 약속, 규칙입니다. 이터레이터 프로토콜은 "값을 순차적으로 꺼내는 방법에 대한 약속"입니다.
이 프로토콜의 핵심은 딱 두 가지입니다. **iter()**와 **next()**입니다.
iter()는 객체에서 이터레이터를 꺼내고, next()는 이터레이터에서 다음 값을 꺼냅니다. 더 꺼낼 값이 없으면 StopIteration 예외가 발생합니다.
위 코드를 따라가 봅시다. 리스트 [10, 20, 30]에 iter()를 호출하면 이터레이터 객체가 반환됩니다.
이 객체에 next()를 호출하면 10이 나오고, 다시 호출하면 20, 또 호출하면 30이 나옵니다. 네 번째 호출에서는 StopIteration이 발생합니다.
놀라운 사실은, for문이 바로 이 과정을 자동화한 것이라는 점입니다. for color in colors:는 내부적으로 iter(colors)를 호출하고, 반복할 때마다 next()를 호출합니다.
StopIteration이 발생하면 루프를 종료합니다. 두 번째 코드 블록이 바로 for문의 실제 동작입니다.
이것을 이해하면 왜 다양한 객체가 for문에서 동작하는지 알 수 있습니다. 리스트, 튜플, 문자열, 딕셔너리, set, 파일 객체 등은 모두 이터레이터 프로토콜을 구현하고 있습니다.
iter()와 next()에 응답할 수 있으면 for문에서 쓸 수 있습니다. 이터러블과 이터레이터는 다른 개념입니다.
이터러블은 iter()를 호출할 수 있는 객체이고, 이터레이터는 next()를 호출할 수 있는 객체입니다. 리스트는 이터러블이고, iter(리스트)의 결과가 이터레이터입니다.
제너레이터는 이터레이터이면서 동시에 이터러블입니다. 실무에서 이 지식은 커스텀 순회 객체를 만들 때 필요합니다.
예를 들어 데이터베이스에서 결과를 한 건씩 가져오는 객체, 네트워크 스트림에서 데이터를 읽는 객체 등을 만들 때 이터레이터 프로토콜을 구현하면 for문으로 자연스럽게 순회할 수 있습니다. 주의할 점이 있습니다.
이터레이터는 상태를 가집니다. 현재 어디까지 읽었는지 기억하고 있어서, 한 번 끝까지 순회하면 다시 처음으로 돌아갈 수 없습니다.
다시 순회하려면 iter()로 새 이터레이터를 만들어야 합니다. 김개발 씨는 이터레이터 프로토콜을 이해하고 나니, for문이 새롭게 보였습니다.
"단순히 문법이 아니라, 프로토콜 기반의 추상화였군요!" 파이썬의 우아함을 한층 더 깊이 이해하게 된 순간이었습니다.
실전 팁
💡 - for문은 내부적으로 iter()와 next()를 사용합니다
- 이터레이터는 상태를 가지므로 한 번 소진되면 재사용이 불가능합니다
- 이터러블은 iter()가 가능한 객체, 이터레이터는 next()가 가능한 객체입니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.
보안 아키텍처 구성 완벽 가이드
프로젝트의 보안을 처음부터 설계하는 방법을 배웁니다. AWS 환경에서 VPC부터 WAF, 암호화, 접근 제어까지 실무에서 바로 적용할 수 있는 보안 아키텍처를 단계별로 구성해봅니다.
AWS Organizations 완벽 가이드
여러 AWS 계정을 체계적으로 관리하고 통합 결제와 보안 정책을 적용하는 방법을 실무 스토리로 쉽게 배워봅니다. 초보 개발자도 바로 이해할 수 있는 친절한 설명과 실전 예제를 제공합니다.
AWS KMS 암호화 완벽 가이드
AWS KMS(Key Management Service)를 활용한 클라우드 데이터 암호화 방법을 초급 개발자를 위해 쉽게 설명합니다. CMK 생성부터 S3, EBS 암호화, 봉투 암호화까지 실무에 필요한 모든 내용을 담았습니다.
AWS Secrets Manager 완벽 가이드
AWS에서 데이터베이스 비밀번호, API 키 등 민감한 정보를 안전하게 관리하는 Secrets Manager의 핵심 개념과 실무 활용법을 배워봅니다. 초급 개발자도 쉽게 따라할 수 있도록 실전 예제와 함께 설명합니다.