Python map() 함수: 예제로 배우는 이터러블 변환
Updated on
리스트의 모든 요소를 변환하는 것은 Python 프로그래밍에서 가장 흔한 작업 중 하나입니다. 정수로 변환해야 하는 문자열 리스트가 있습니다. 반올림해야 하는 가격 열이 있습니다. 소문자로 변환해야 하는 파일 이름 집합이 있습니다. 본능적으로 for 루프를 작성하고, 빈 리스트를 만들고, 각 결과를 추가하고, 리스트를 반환합니다. 작동하지만 장황하고, 오류가 발생하기 쉬우며, 실제 변환 로직을 보일러플레이트 코드 안에 묻어버립니다.
Python의 내장 map() 함수는 이 보일러플레이트를 제거합니다. 함수와 하나 이상의 이터러블을 받아 모든 요소에 함수를 적용하고 결과의 이터레이터를 반환합니다. 한 줄이 다섯 줄을 대체합니다. 코드는 어떻게 하는지가 아니라 무엇을 원하는지의 설명처럼 읽힙니다.
이 가이드는 Python map 함수의 모든 실용적 측면을 다룹니다: 기본 구문, lambda 및 내장 함수와의 map 결합, 다중 이터러블 작업, 리스트 컴프리헨션과의 성능 비교, 그리고 프로덕션 코드에서 사용할 실제 데이터 처리 패턴입니다.
map()의 기본 구문
map() 함수의 시그니처는 간단합니다:
map(function, iterable, *iterables)- function -- 하나의 인수를 받는 호출 가능한 객체 (다중 이터러블을 전달하면 더 많이).
- iterable -- 요소가
function에 전달될 시퀀스. - *iterables -- 선택적 추가 이터러블. 제공 시
function은 그만큼의 인수를 받아야 합니다.
map()은 지연 이터레이터인 map 객체를 반환합니다. 모든 결과를 즉시 계산하지 않습니다. 대신 소비할 때 한 번에 하나씩 값을 생성합니다.
가장 간단한 예시입니다:
numbers = [1, 2, 3, 4, 5]
squared = map(lambda x: x ** 2, numbers)
print(squared)
# <map object at 0x...>
print(list(squared))
# [1, 4, 9, 16, 25]map 객체는 결과를 직접 표시하지 않습니다. 리스트, 튜플 또는 다른 컬렉션으로 변환하거나 루프에서 반복합니다.
map()과 내장 함수
map()의 가장 깔끔한 사용법 중 하나는 Python의 내장 함수와 결합하는 것입니다. lambda가 필요 없습니다 -- 함수 이름을 직접 전달하면 됩니다.
int, float, str로 타입 변환
# 문자열을 정수로 변환
string_numbers = ["10", "20", "30", "40"]
integers = list(map(int, string_numbers))
print(integers)
# [10, 20, 30, 40]
# 정수를 부동소수점으로 변환
values = [1, 2, 3, 4]
floats = list(map(float, values))
print(floats)
# [1.0, 2.0, 3.0, 4.0]
# 숫자를 문자열로 변환
ids = [101, 102, 103]
string_ids = list(map(str, ids))
print(string_ids)
# ['101', '102', '103']len으로 길이 구하기
words = ["Python", "map", "function", "tutorial"]
lengths = list(map(len, words))
print(lengths)
# [6, 3, 8, 8]str.strip으로 공백 제거
raw_inputs = [" hello ", " world ", " python "]
cleaned = list(map(str.strip, raw_inputs))
print(cleaned)
# ['hello', 'world', 'python']기타 유용한 내장 함수 조합
# abs()로 절대값
numbers = [-5, 3, -1, 7, -2]
absolutes = list(map(abs, numbers))
print(absolutes)
# [5, 3, 1, 7, 2]
# round()로 반올림 (단일 인수 형태)
prices = [19.995, 4.123, 99.876]
rounded = list(map(round, prices))
print(rounded)
# [20, 4, 100]
# bool()로 참/거짓 판별
values = [0, 1, "", "hello", None, [], [1]]
truthy = list(map(bool, values))
print(truthy)
# [False, True, False, True, False, False, True]내장 함수가 이미 필요한 작업을 수행한다면 map()에 직접 전달하세요. lambda로 감쌀 이유가 없습니다.
map()과 Lambda 함수
Lambda 함수는 map()의 모든 힘을 끌어냅니다. 이름 있는 함수를 만들지 않고 인라인 변환을 정의할 수 있습니다.
# 각 숫자 제곱
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))
print(squared)
# [1, 4, 9, 16, 25]
# 섭씨에서 화씨로
celsius = [0, 20, 37, 100]
fahrenheit = list(map(lambda c: round(c * 9/5 + 32, 1), celsius))
print(fahrenheit)
# [32.0, 68.0, 98.6, 212.0]
# 이메일 주소에서 도메인 추출
emails = ["alice@gmail.com", "bob@company.org", "carol@university.edu"]
domains = list(map(lambda e: e.split("@")[1], emails))
print(domains)
# ['gmail.com', 'company.org', 'university.edu']
# 이름 포맷팅
names = ["alice smith", "bob jones", "carol white"]
formatted = list(map(lambda n: n.title(), names))
print(formatted)
# ['Alice Smith', 'Bob Jones', 'Carol White']패턴은 일관적입니다: map(lambda x: transform(x), iterable). lambda 본문은 단일하고 명확한 표현식으로 유지하세요. 변환에 여러 단계가 필요하면 대신 이름 있는 함수를 사용하세요.
map()과 다중 이터러블
여러 이터러블을 전달하면 map()은 각 이터러블에서 하나의 요소를 가져와 병렬로 함수를 호출합니다. 함수는 이터러블 수만큼의 인수를 받아야 합니다. 반복은 가장 짧은 이터러블이 소진되면 멈춥니다.
# 두 리스트의 대응하는 요소 더하기
list_a = [1, 2, 3, 4]
list_b = [10, 20, 30, 40]
sums = list(map(lambda a, b: a + b, list_a, list_b))
print(sums)
# [11, 22, 33, 44]
# 가중 점수 계산
scores = [85, 92, 78, 95]
weights = [0.3, 0.3, 0.2, 0.2]
weighted = list(map(lambda s, w: round(s * w, 1), scores, weights))
print(weighted)
# [25.5, 27.6, 15.6, 19.0]
# 이름과 성 결합
first_names = ["Ada", "Grace", "Alan"]
last_names = ["Lovelace", "Hopper", "Turing"]
full_names = list(map(lambda f, l: f"{f} {l}", first_names, last_names))
print(full_names)
# ['Ada Lovelace', 'Grace Hopper', 'Alan Turing']세 개 이상의 이터러블
# 세 개의 치수 리스트에서 부피 계산
lengths = [2, 3, 4]
widths = [5, 6, 7]
heights = [8, 9, 10]
volumes = list(map(lambda l, w, h: l * w * h, lengths, widths, heights))
print(volumes)
# [80, 162, 280]길이가 다른 경우 처리
map()은 가장 짧은 이터러블에서 멈춥니다. 에러 없이, 패딩 없이 -- 단순히 멈춥니다:
a = [1, 2, 3, 4, 5]
b = [10, 20, 30]
result = list(map(lambda x, y: x + y, a, b))
print(result)
# [11, 22, 33]
# 'a'의 요소 4와 5는 조용히 무시됨길이가 다른 경우를 명시적으로 처리해야 한다면 itertools.zip_longest를 사용하세요:
from itertools import zip_longest
a = [1, 2, 3, 4, 5]
b = [10, 20, 30]
result = list(map(lambda pair: pair[0] + (pair[1] or 0), zip_longest(a, b, fillvalue=0)))
print(result)
# [11, 22, 33, 4, 5]map() vs 리스트 컴프리헨션
가장 중요한 비교입니다. map()과 리스트 컴프리헨션 모두 시퀀스를 변환하지만 서로 다른 강점을 가지고 있습니다.
| 특성 | map() | 리스트 컴프리헨션 |
|---|---|---|
| 구문 | map(func, iterable) | [func(x) for x in iterable] |
| 가독성 (간단한 변환) | 내장 함수와 높음 | 모든 경우에 높음 |
| 가독성 (복잡한 변환) | 낮음 (중첩된 lambda) | 높음 (여러 줄 가능) |
| 필터링 지원 | 없음 (filter() 별도 필요) | 있음 (if 절 내장) |
| 반환 타입 | 지연 이터레이터 (map 객체) | 즉시 리스트 |
| 대용량 데이터 메모리 | 낮음 (지연 평가) | 높음 (전체 리스트가 메모리에) |
| 속도 (내장 함수) | 빠름 (Python 레벨 호출 없음) | 약간 느림 |
| 속도 (lambda 함수) | 비슷함 | 비슷하거나 약간 빠름 |
| 중첩 변환 | 체인이 불편함 | 중첩이 자연스러움 |
| 디버깅 | 어려움 (지연, 중간 리스트 없음) | 쉬움 (즉시 결과) |
성능 벤치마크
import timeit
data = list(range(100_000))
# 내장 함수로 map
time_map_builtin = timeit.timeit(
'list(map(str, data))',
globals={'data': data},
number=100
)
# 내장 함수로 리스트 컴프리헨션
time_comp_builtin = timeit.timeit(
'[str(x) for x in data]',
globals={'data': data},
number=100
)
# lambda로 map
time_map_lambda = timeit.timeit(
'list(map(lambda x: x * 2, data))',
globals={'data': data},
number=100
)
# 리스트 컴프리헨션 등가
time_comp_expr = timeit.timeit(
'[x * 2 for x in data]',
globals={'data': data},
number=100
)
print(f"map + 내장: {time_map_builtin:.4f}s")
print(f"컴프리헨션: {time_comp_builtin:.4f}s")
print(f"map + lambda: {time_map_lambda:.4f}s")
print(f"컴프리헨션: {time_comp_expr:.4f}s")
# 일반적인 결과:
# map + 내장: ~0.85s (더 빠름)
# 컴프리헨션: ~1.05s
# map + lambda: ~0.95s
# 컴프리헨션: ~0.80s (더 빠름)핵심 통찰: map()을 내장 함수(int, str, float 등)와 사용하면 각 반복에서 Python 레벨의 함수 호출을 피하기 때문에 더 빠릅니다. 하지만 map()을 lambda와 사용하면 리스트 컴프리헨션과 거의 같은 속도이거나 약간 느립니다. 둘 다 요소당 Python 레벨의 함수 호출을 수반하기 때문입니다.
map() vs 제너레이터 표현식
전체 결과 리스트를 메모리에 보관할 필요가 없다면 map()과 제너레이터 표현식 모두 지연 평가를 제공합니다:
# map 객체 - 지연
mapped = map(lambda x: x ** 2, range(1_000_000))
# 제너레이터 표현식 - 지연
generated = (x ** 2 for x in range(1_000_000))
# 둘 다 반복할 때만 메모리를 소비
for value in mapped:
if value > 100:
break차이는 스타일적입니다. 제너레이터 표현식은 대부분의 Python 개발자에게 더 자연스럽게 읽히며 if 절로 필터링을 지원합니다. 적용할 이름 있는 함수가 이미 있으면 map()을 사용하고, 그렇지 않으면 제너레이터 표현식을 사용하세요.
# 이름 있는 함수가 있으면 map이 깔끔
import math
roots = map(math.sqrt, range(10))
# 표현식에는 제너레이터가 깔끔
roots = (x ** 0.5 for x in range(10))map 객체를 list, tuple, set으로 변환
map 객체는 이터레이터입니다. 결과를 구체화하려면 명시적으로 변환합니다:
numbers = [1, 2, 3, 2, 4, 3, 5]
# 리스트로 (순서 유지, 중복 허용)
as_list = list(map(lambda x: x * 10, numbers))
print(as_list)
# [10, 20, 30, 20, 40, 30, 50]
# 튜플로 (불변 시퀀스)
as_tuple = tuple(map(lambda x: x * 10, numbers))
print(as_tuple)
# (10, 20, 30, 20, 40, 30, 50)
# 세트로 (중복 제거, 순서 없음)
as_set = set(map(lambda x: x * 10, numbers))
print(as_set)
# {10, 20, 30, 40, 50}중요: map 객체는 한 번만 소비할 수 있습니다. 리스트로 변환한 후 다시 반복하면 아무것도 나오지 않습니다:
mapped = map(str, [1, 2, 3])
first = list(mapped)
second = list(mapped)
print(first) # ['1', '2', '3']
print(second) # [] -- 이미 소진됨map()과 커스텀 함수
lambda에는 너무 복잡한 변환의 경우, 일반 함수를 정의하고 map()에 전달합니다:
def parse_temperature(reading):
"""온도 읽기 문자열을 표준화된 딕셔너리로 변환."""
value, unit = float(reading[:-1]), reading[-1]
if unit == 'F':
celsius = round((value - 32) * 5 / 9, 1)
elif unit == 'C':
celsius = value
elif unit == 'K':
celsius = round(value - 273.15, 1)
else:
raise ValueError(f"알 수 없는 단위: {unit}")
return {"original": reading, "celsius": celsius}
readings = ["98.6F", "37.0C", "310.0K", "72.0F"]
parsed = list(map(parse_temperature, readings))
for entry in parsed:
print(f"{entry['original']} -> {entry['celsius']}°C")
# 98.6F -> 37.0°C
# 37.0C -> 37.0°C
# 310.0K -> 36.9°C
# 72.0F -> 22.2°C이 패턴은 map() 호출을 깔끔하게 유지하면서 복잡한 로직을 테스트 가능하고 문서화된 함수로 이동합니다.
def normalize_record(record):
"""사용자 레코드를 데이터베이스 삽입용으로 표준화."""
return {
"name": record["name"].strip().title(),
"email": record["email"].strip().lower(),
"age": int(record["age"]),
"active": record.get("active", True),
}
raw_records = [
{"name": " alice smith ", "email": "ALICE@Email.COM", "age": "30"},
{"name": "bob jones", "email": "Bob@Work.ORG ", "age": "25"},
]
clean_records = list(map(normalize_record, raw_records))
for r in clean_records:
print(r)
# {'name': 'Alice Smith', 'email': 'alice@email.com', 'age': 30, 'active': True}
# {'name': 'Bob Jones', 'email': 'bob@work.org', 'age': 25, 'active': True}map() 호출 체이닝
map()이 이터레이터를 반환하므로 여러 map() 호출을 체인하여 변환 파이프라인을 구축할 수 있습니다. 각 단계는 중간 리스트를 만들지 않고 한 번에 하나의 요소를 처리합니다:
raw_data = [" 42 ", " 17 ", " 89 ", " 3 "]
# 체인: 공백 제거 -> int로 변환 -> 값 두 배
result = map(str.strip, raw_data)
result = map(int, result)
result = map(lambda x: x * 2, result)
print(list(result))
# [84, 34, 178, 6]가독성을 위해 호출을 중첩할 수 있습니다 (안쪽에서 바깥쪽으로 읽기):
raw_data = [" 42 ", " 17 ", " 89 ", " 3 "]
result = list(map(lambda x: x * 2, map(int, map(str.strip, raw_data))))
print(result)
# [84, 34, 178, 6]중첩 버전은 컴팩트하지만 읽기 어렵습니다. 순차 버전은 다단계 파이프라인에 더 명확합니다. 어느 쪽도 중간 리스트를 만들지 않습니다 -- 세 개의 map 객체 모두 지연 평가됩니다.
데이터 처리 파이프라인에서의 map()
실제 데이터 처리는 종종 데이터 읽기, 정리, 변환, 집계를 포함합니다. map()은 이 패턴에 자연스럽게 맞습니다.
로그 항목 처리
log_lines = [
"2026-02-10 INFO User logged in",
"2026-02-10 ERROR Database timeout",
"2026-02-10 WARNING Disk space low",
"2026-02-10 INFO File uploaded",
]
def parse_log(line):
parts = line.split(" ", 2)
return {"date": parts[0], "level": parts[1], "message": parts[2]}
parsed = list(map(parse_log, log_lines))
errors = list(filter(lambda entry: entry["level"] == "ERROR", parsed))
print(errors)
# [{'date': '2026-02-10', 'level': 'ERROR', 'message': 'Database timeout'}]일괄 파일 이름 변경 로직
import os
filenames = ["Report Q1.PDF", "data export.CSV", "NOTES 2026.TXT"]
def sanitize_filename(name):
base, ext = os.path.splitext(name)
return base.strip().lower().replace(" ", "_") + ext.lower()
cleaned = list(map(sanitize_filename, filenames))
print(cleaned)
# ['report_q1.pdf', 'data_export.csv', 'notes_2026.txt']수치 데이터 파이프라인
import math
raw_measurements = ["3.14159", "2.71828", "1.41421", "1.73205"]
# 파이프라인: 파싱 -> 제곱 -> 로그 -> 소수점 3자리 반올림
pipeline = map(lambda x: round(x, 3),
map(math.log,
map(lambda x: x ** 2,
map(float, raw_measurements))))
print(list(pipeline))
# [2.292, 2.003, 0.693, 1.099]map() vs 리스트 컴프리헨션 사용 가이드
상황에 맞는 도구를 선택하기 위한 빠른 참조:
map()을 사용할 때:
- 단일 내장 함수를 적용:
map(int, strings),map(str.strip, lines) - 변환을 수행하는 이름 있는 함수가 이미 있을 때
- 지연 평가가 필요하고 전체 리스트를 메모리에 보관할 필요가 없을 때
- 요소별 연산을 위해 여러 이터러블을 전달할 때
리스트 컴프리헨션을 사용할 때:
- 변환이 단순한 표현식:
[x * 2 for x in numbers] - 같은 단계에서 필터링 필요:
[x for x in items if x > 0] - 로직이 여러 조건이나 중첩된 연산을 포함할 때
- 결과를 즉시 리스트로 원할 때
- 마이크로 최적화보다 가독성이 중요할 때
제너레이터 표현식을 사용할 때:
- 지연 평가가 필요하지만 컴프리헨션 구문의 가독성을 원할 때
- 결과가 한 번만 소비될 때 (
sum(),max(),join()등에 전달)
# map -- 타입 변환에 가장 깔끔
integers = list(map(int, ["1", "2", "3"]))
# 리스트 컴프리헨션 -- 변환 + 필터에 가장 깔끔
even_squares = [x ** 2 for x in range(20) if x % 2 == 0]
# 제너레이터 표현식 -- 단일 패스 소비에 가장 깔끔
total = sum(x ** 2 for x in range(1000))RunCell에서 map() 실험하기
map()으로 데이터 변환 파이프라인을 구축할 때 각 단계를 대화식으로 테스트하는 것이 필수적입니다. 중간 결과를 확인하고, 엣지 케이스를 점검하고, 체인된 map이 예상 출력을 생성하는지 검증해야 합니다.
RunCell (opens in a new tab)은 Jupyter 노트북 내에서 직접 실행되는 AI 에이전트입니다. 노트북 컨텍스트 -- 메모리에 있는 변수, DataFrame, 임포트 -- 를 이해하고 map() 파이프라인을 단계별로 구축하고 디버그하는 것을 도와줍니다:
- 중간 결과 시각화. 체인된
map()의 각 단계가 무엇을 생성하는지 RunCell에 표시하도록 요청하세요. 파이프라인을 깨뜨리지 않고 확인할 수 있습니다. - 벡터화된 대안 제안. pandas Series에서
map()을 사용하고 있다면 RunCell은 10~100배 빠른 네이티브 pandas 연산을 추천할 수 있습니다. - map과 컴프리헨션 간 변환. 변환을 설명하면 RunCell이 두 버전을 모두 생성하여 더 읽기 쉬운 것을 선택할 수 있습니다.
- 엣지 케이스 디버그. 예상치 못한 입력(None 값, 빈 문자열, 혼합 타입)을 map 파이프라인에 투입하고 정확히 어디서 깨지는지 확인할 수 있습니다.
FAQ
Python의 map() 함수는 무엇을 하나요?
map() 함수는 하나 이상의 이터러블(리스트, 튜플, 문자열 등)의 각 항목에 주어진 함수를 적용하고 결과의 이터레이터를 반환합니다. 명시적 루프를 작성하지 않고 함수형 스타일의 데이터 변환을 가능하게 하는 내장 함수입니다. 구문은 map(function, iterable)이며, 반환된 map 객체는 지연형입니다 -- 한 번에 모두가 아니라 필요할 때 결과를 계산합니다.
Python에서 map()이 for 루프보다 빠른가요?
네, map()은 append()로 리스트를 구축하는 동등한 for 루프보다 일반적으로 빠릅니다. 속도 이점은 map()이 인터프리터 레벨에서 C로 구현되어 Python 레벨의 루프 반복과 메서드 호출의 오버헤드를 피하는 데서 옵니다. int이나 str 같은 내장 함수와 map()을 사용하면 성능 이점이 가장 큽니다 -- 일반적으로 20~40% 빠릅니다. lambda의 경우 lambda 자체가 Python 레벨의 함수 호출을 도입하므로 차이가 줄어듭니다.
map 객체를 리스트로 변환하려면 어떻게 하나요?
map() 호출을 list()로 감쌉니다: result = list(map(int, strings)). 다른 컬렉션으로도 변환할 수 있습니다: 튜플은 tuple(map(...)), 세트는 set(map(...)). map 객체는 일회용 이터레이터임을 기억하세요. 소비된 후(리스트로 변환하거나 반복한 후)에는 소진되어 재사용할 수 없습니다.
map()은 여러 리스트와 함께 사용할 수 있나요?
네. 함수 뒤에 여러 이터러블을 전달합니다: map(func, list1, list2, list3). 함수는 이터러블 수만큼의 인수를 받아야 합니다. 예를 들어 list(map(lambda a, b: a + b, [1, 2], [10, 20]))은 [11, 22]를 반환합니다. 반복은 가장 짧은 이터러블에서 멈추므로 길이가 다른 리스트도 에러를 발생시키지 않습니다 -- 추가 요소는 조용히 무시됩니다.
map()과 리스트 컴프리헨션 중 어떤 것을 사용해야 하나요?
단일 기존 함수(내장 또는 이름 있는)를 이터러블에 적용할 때, 특히 map(int, strings) 같은 타입 변환에는 map()을 사용하세요. 변환이 표현식을 포함하거나 if 절로 필터링이 필요하거나 가독성이 우선일 때는 리스트 컴프리헨션을 사용하세요. 현대 Python에서 리스트 컴프리헨션이 대부분의 개발자의 기본 선택이지만, 내장 함수와의 map()은 단순한 단일 함수 변환에 여전히 더 빠르고 간결합니다.
결론
Python의 map() 함수는 특정 작업을 위한 정밀한 도구입니다: 루프를 작성하지 않고 이터러블의 모든 요소에 단일 함수를 적용하는 것. int, str, float, len 같은 내장 함수와 결합하면 가장 읽기 쉽고 빠른 코드를 생성합니다. lambda 함수와는 인라인 변환을 깔끔하게 처리합니다. 다중 이터러블과는 zip()과 루프가 필요한 요소별 연산을 처리합니다.
map()을 효과적으로 사용하기 위한 규칙:
- 내장 함수를 직접 전달:
map(int, strings)(O)map(lambda x: int(x), strings)(X). - lambda는 단순한 단일 표현식 변환에만 사용.
- 변환에 에러 처리, 여러 단계 또는 문서화가 필요하면 이름 있는 함수로 전환.
- 필터링이 필요하거나 표현식이 인라인으로 더 명확한 경우 리스트 컴프리헨션 선호.
map()이 지연형 일회용 이터레이터를 반환함을 기억 -- 결과를 재사용하거나 검사해야 하면list()로 변환.- 중간 리스트를 만들지 않는 다단계 파이프라인을 위해
map()호출 체인.
map()은 리스트 컴프리헨션의 대체물이 아니며, 리스트 컴프리헨션은 map()의 대체물이 아닙니다. 겹치지만 서로 다른 강점에 봉사합니다. 최고의 Python 코드는 각각이 자연스럽게 맞는 곳에서 사용합니다: 기존 함수 적용에는 map(), 표현식과 필터링에는 리스트 컴프리헨션, 지연형 단일 패스 소비에는 제너레이터 표현식입니다.