한 줄로 간단한 함수를 만드는 람다! 간결하고 강력한 익명 함수의 세계로 빠져봅시다. (30분 완독 ⭐⭐⭐)
🎯 오늘의 학습 목표
- 람다 함수의 개념 이해하기
- 람다 표현식 작성하기
- sorted()와 map(), filter() 마스터하기
- 람다 조합 패턴 익히기
- 람다 실전 활용과 주의사항
📚 사전 지식
🎯 학습 목표 1: 람다 함수의 개념 이해하기
한 줄 설명
람다 함수 = 이름이 없는 간단한 함수 (익명 함수, Anonymous Function)
한 줄 표현식으로 함수를 정의할 수 있는 간결한 문법입니다.
실생활 비유
1
2
3
4
5
6
7
8
9
| 🏪 일반 가게 (def 함수):
- 간판이 있음 (이름)
- 여러 상품 판매 가능 (여러 줄 코드)
- 언제든 다시 방문 가능 (재사용)
🛒 팝업 스토어 (lambda 함수):
- 간판 없음 (익명)
- 한 가지만 판매 (한 줄 표현식)
- 임시로 사용 (일회성)
|
왜 람다 함수를 사용할까?
1. 간결성 (Conciseness)
1
2
3
4
5
6
7
8
| # def 함수: 4줄
def double(x):
return x * 2
result = double(5) # 10
# lambda: 1줄
result = (lambda x: x * 2)(5) # 10
|
2. 일회성 함수 (Disposable Functions)
1
2
3
4
5
6
7
8
9
10
11
| # 정렬할 때 한 번만 사용하는 함수
students = [("홍길동", 85), ("김철수", 92), ("이영희", 78)]
# def 함수 방식
def get_score(student):
return student[1]
sorted_students = sorted(students, key=get_score)
# lambda 방식 (더 간결)
sorted_students = sorted(students, key=lambda s: s[1])
|
3. 고차 함수와의 조합 (Higher-Order Functions)
람다는 map(), filter(), sorted() 등 다른 함수의 인자로 전달될 때 매우 유용합니다.
1
2
3
4
5
6
7
8
9
| numbers = [1, 2, 3, 4, 5]
# map과 lambda: 모든 수를 제곱
squares = list(map(lambda x: x ** 2, numbers))
print(squares) # [1, 4, 9, 16, 25]
# filter와 lambda: 짝수만 선택
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # [2, 4]
|
람다의 역사와 유래
람다(λ) 는 그리스 문자로, 1930년대 수학자 Alonzo Church가 개발한 람다 대수(Lambda Calculus) 에서 유래했습니다.
1
2
3
4
5
| 수학적 표현:
λx.x + 1 → "x를 받아서 x+1을 반환하는 함수"
Python 표현:
lambda x: x + 1
|
람다 대수는 현대 함수형 프로그래밍의 기초가 되었고, Python뿐만 아니라 JavaScript, Java, C# 등 많은 언어가 이 개념을 채택했습니다.
람다 함수의 특징 정리
| 특징 | 설명 | 예시 |
| 익명성 | 이름이 없음 (변수에 할당 가능) | f = lambda x: x + 1 |
| 단일 표현식 | 한 줄의 표현식만 가능 | lambda x: x ** 2 |
| 암묵적 반환 | return 키워드 불필요 | lambda x, y: x + y |
| 일회성 | 주로 한 번만 사용 | sorted(lst, key=lambda x: x[1]) |
| 제한적 | 여러 문장, 할당문 불가 | lambda x: y = x + 1 |
🎯 학습 목표 2: lambda 문법 익히기
기본 문법 구조
구성 요소:
lambda 키워드로 시작 - 매개변수는 콤마(
,)로 구분 - 콜론(
:) 뒤에 단일 표현식 return 키워드 불필요 (자동으로 반환)
def vs lambda 상세 비교
예제 1: 단일 인자
1
2
3
4
5
6
7
8
9
10
11
12
13
| # ✅ 일반 함수 (def)
def square(x):
return x ** 2
print(square(5)) # 25
# ✅ 람다 함수 (lambda)
square_lambda = lambda x: x ** 2
print(square_lambda(5)) # 25
# ✅ 람다를 변수에 할당하지 않고 바로 호출
print((lambda x: x ** 2)(5)) # 25
|
예제 2: 다중 인자
1
2
3
4
5
6
7
8
9
10
| # def 함수
def add(x, y):
return x + y
print(add(3, 5)) # 8
# lambda 함수
add_lambda = lambda x, y: x + y
print(add_lambda(3, 5)) # 8
|
예제 3: 복잡한 표현식
1
2
3
4
5
6
7
8
9
10
| # def 함수
def calculate(x, y, z):
return (x + y) * z
print(calculate(2, 3, 4)) # 20
# lambda 함수
calculate_lambda = lambda x, y, z: (x + y) * z
print(calculate_lambda(2, 3, 4)) # 20
|
문법 비교표
| 특징 | def 함수 | lambda 함수 |
| 이름 | 필수 | 선택 (변수에 할당 가능) |
| 줄 수 | 여러 줄 가능 | 단 한 줄만 |
| return | 명시적 return | 암묵적 반환 |
| 문서화 | Docstring 가능 | 불가능 |
| 재사용 | 언제든지 호출 | 주로 일회성 |
| 디버깅 | 함수 이름으로 추적 | 익명이라 어려움 |
람다 함수 작성 방법
방법 1: 변수에 할당
1
2
3
4
5
| # 람다를 변수에 저장
multiply = lambda x, y: x * y
print(multiply(3, 4)) # 12
print(multiply(5, 6)) # 30
|
언제 사용: 람다를 여러 번 재사용해야 할 때 (하지만 이런 경우 def를 쓰는 게 더 좋습니다)
방법 2: 즉시 실행
1
2
3
| # 람다를 정의하고 바로 호출
result = (lambda x, y: x + y)(10, 20)
print(result) # 30
|
언제 사용: 딱 한 번만 사용할 계산 (매우 드뭅니다)
방법 3: 고차 함수의 인자 (가장 일반적)
1
2
3
4
5
6
| # sorted의 key 인자로 전달
students = [("홍길동", 85), ("김철수", 92), ("이영희", 78)]
sorted_students = sorted(students, key=lambda s: s[1])
print(sorted_students)
# [('이영희', 78), ('홍길동', 85), ('김철수', 92)]
|
언제 사용: map(), filter(), sorted(), max(), min() 등과 함께 (가장 권장되는 사용법!)
매개변수 패턴
1. 매개변수 없음
1
2
3
4
| # 매개변수 없이도 정의 가능
get_pi = lambda: 3.14159
print(get_pi()) # 3.14159
|
2. 단일 매개변수
1
2
3
4
| # 가장 기본적인 형태
square = lambda x: x ** 2
print(square(7)) # 49
|
3. 다중 매개변수
1
2
3
4
| # 여러 매개변수 (콤마로 구분)
add_three = lambda a, b, c: a + b + c
print(add_three(1, 2, 3)) # 6
|
4. 기본값이 있는 매개변수
1
2
3
4
5
| # 기본값 지정 가능
greet = lambda name, greeting="안녕하세요": f"{greeting}, {name}님!"
print(greet("홍길동")) # 안녕하세요, 홍길동님!
print(greet("김철수", "환영합니다")) # 환영합니다, 김철수님!
|
조건 표현식과 람다
람다 안에서 if-else를 사용할 수 있습니다 (삼항 연산자).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # 절댓값 계산
abs_value = lambda x: x if x >= 0 else -x
print(abs_value(5)) # 5
print(abs_value(-5)) # 5
# 짝수/홀수 판별
is_even = lambda x: "짝수" if x % 2 == 0 else "홀수"
print(is_even(4)) # 짝수
print(is_even(7)) # 홀수
# 최댓값 찾기
max_value = lambda a, b: a if a > b else b
print(max_value(10, 20)) # 20
|
주의: if-else 는 표현식(값을 반환)이므로 가능하지만, if 문(Statement)은 불가능합니다.
1
2
3
4
5
| # ❌ 이런 건 불가능 (if 문은 값을 반환하지 않음)
# check = lambda x: if x > 0: return "양수" # SyntaxError
# ✅ 삼항 연산자 사용
check = lambda x: "양수" if x > 0 else "음수 또는 0"
|
💡 람다 함수 예제
예제 1: 간단한 연산
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # 제곱
square = lambda x: x ** 2
print(square(5)) # 25
# 곱하기
multiply = lambda x, y: x * y
print(multiply(3, 4)) # 12
# 세 수의 합
sum_three = lambda a, b, c: a + b + c
print(sum_three(1, 2, 3)) # 6
# 조건 표현식
max_two = lambda a, b: a if a > b else b
print(max_two(10, 20)) # 20
|
예제 2: 문자열 처리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # 문자열 길이
length = lambda s: len(s)
print(length("Python")) # 6
# 대문자 변환
to_upper = lambda s: s.upper()
print(to_upper("hello")) # HELLO
# 문자열 결합
concat = lambda s1, s2: s1 + " " + s2
print(concat("Hello", "World")) # Hello World
# 첫 글자 추출
first_char = lambda s: s[0] if s else ""
print(first_char("Python")) # P
print(first_char("")) # (빈 문자열)
|
예제 3: 리스트 처리
1
2
3
4
5
6
7
8
9
10
11
| # 리스트 길이
list_len = lambda lst: len(lst)
print(list_len([1, 2, 3, 4, 5])) # 5
# 리스트 합계
list_sum = lambda lst: sum(lst)
print(list_sum([1, 2, 3, 4, 5])) # 15
# 리스트 평균
list_avg = lambda lst: sum(lst) / len(lst) if lst else 0
print(list_avg([10, 20, 30])) # 20.0
|
🎯 학습 목표 3: sorted()와 map(), filter() 마스터하기
sorted() - 기본 정렬
1
2
3
4
5
6
7
8
9
| numbers = [5, 2, 8, 1, 9]
# 오름차순 (기본)
sorted_asc = sorted(numbers)
print(sorted_asc) # [1, 2, 5, 8, 9]
# 내림차순
sorted_desc = sorted(numbers, reverse=True)
print(sorted_desc) # [9, 8, 5, 2, 1]
|
복잡한 정렬 (key 사용)
1
2
3
4
5
6
| # 문자열 길이로 정렬
words = ["Python", "is", "awesome", "and", "powerful"]
sorted_words = sorted(words, key=lambda w: len(w))
print(sorted_words)
# ['is', 'and', 'Python', 'awesome', 'powerful']
|
튜플 리스트 정렬
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # 학생 (이름, 점수) 튜플
students = [
("홍길동", 85),
("김철수", 92),
("이영희", 78),
("박민수", 88)
]
# 점수로 정렬 (오름차순)
sorted_by_score = sorted(students, key=lambda s: s[1])
print(sorted_by_score)
# [('이영희', 78), ('홍길동', 85), ('박민수', 88), ('김철수', 92)]
# 이름으로 정렬
sorted_by_name = sorted(students, key=lambda s: s[0])
print(sorted_by_name)
# [('김철수', 92), ('박민수', 88), ('이영희', 78), ('홍길동', 85)]
|
딕셔너리 리스트 정렬
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| students = [
{"name": "홍길동", "age": 25, "score": 85},
{"name": "김철수", "age": 22, "score": 92},
{"name": "이영희", "age": 24, "score": 78},
{"name": "박민수", "age": 23, "score": 88}
]
# 점수로 정렬
sorted_by_score = sorted(students, key=lambda s: s["score"])
for student in sorted_by_score:
print(f"{student['name']}: {student['score']}점")
# 이영희: 78점
# 홍길동: 85점
# 박민수: 88점
# 김철수: 92점
# 나이로 정렬
sorted_by_age = sorted(students, key=lambda s: s["age"])
# 이름으로 정렬
sorted_by_name = sorted(students, key=lambda s: s["name"])
|
복합 정렬
1
2
3
4
5
| # 나이순, 같으면 점수순
sorted_complex = sorted(students, key=lambda s: (s["age"], s["score"]))
# 점수 내림차순
sorted_desc = sorted(students, key=lambda s: s["score"], reverse=True)
|
map() 기본
1
2
3
4
5
6
7
8
9
10
| # 기본 사용법
numbers = [1, 2, 3, 4, 5]
# 제곱
squares = list(map(lambda x: x ** 2, numbers))
print(squares) # [1, 4, 9, 16, 25]
# 두 배
doubles = list(map(lambda x: x * 2, numbers))
print(doubles) # [2, 4, 6, 8, 10]
|
문자열 변환
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| words = ["python", "java", "javascript", "c++"]
# 대문자 변환
upper_words = list(map(lambda w: w.upper(), words))
print(upper_words)
# ['PYTHON', 'JAVA', 'JAVASCRIPT', 'C++']
# 길이 계산
lengths = list(map(lambda w: len(w), words))
print(lengths) # [6, 4, 10, 3]
# 첫 글자 추출
first_letters = list(map(lambda w: w[0], words))
print(first_letters) # ['p', 'j', 'j', 'c']
|
여러 리스트 동시 처리
1
2
3
4
5
6
7
8
9
10
11
| # 두 리스트의 같은 위치 요소끼리 계산
numbers1 = [1, 2, 3, 4, 5]
numbers2 = [10, 20, 30, 40, 50]
# 덧셈
sums = list(map(lambda x, y: x + y, numbers1, numbers2))
print(sums) # [11, 22, 33, 44, 55]
# 곱셈
products = list(map(lambda x, y: x * y, numbers1, numbers2))
print(products) # [10, 40, 90, 160, 250]
|
딕셔너리 변환
1
2
3
4
5
6
7
| names = ["홍길동", "김철수", "이영희"]
scores = [85, 92, 78]
# 이름과 점수를 딕셔너리로
students = list(map(lambda n, s: {"name": n, "score": s}, names, scores))
print(students)
# [{'name': '홍길동', 'score': 85}, {'name': '김철수', 'score': 92}, {'name': '이영희', 'score': 78}]
|
filter() - 조건 필터링
1
2
3
4
5
6
7
8
9
10
11
12
13
| numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 짝수만
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # [2, 4, 6, 8, 10]
# 홀수만
odds = list(filter(lambda x: x % 2 == 1, numbers))
print(odds) # [1, 3, 5, 7, 9]
# 5보다 큰 수
greater_than_5 = list(filter(lambda x: x > 5, numbers))
print(greater_than_5) # [6, 7, 8, 9, 10]
|
문자열 필터링
1
2
3
4
5
6
7
8
9
| words = ["Python", "is", "awesome", "and", "powerful"]
# 길이가 5 이상인 단어
long_words = list(filter(lambda w: len(w) >= 5, words))
print(long_words) # ['Python', 'awesome', 'powerful']
# 'a'로 시작하는 단어
a_words = list(filter(lambda w: w.startswith('a'), words))
print(a_words) # ['awesome', 'and']
|
딕셔너리 필터링
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| students = [
{"name": "홍길동", "score": 85},
{"name": "김철수", "score": 92},
{"name": "이영희", "score": 78},
{"name": "박민수", "score": 88}
]
# 80점 이상
passed = list(filter(lambda s: s["score"] >= 80, students))
print(passed)
# [{'name': '홍길동', 'score': 85}, {'name': '김철수', 'score': 92}, {'name': '박민수', 'score': 88}]
# 90점 이상 (우등생)
excellent = list(filter(lambda s: s["score"] >= 90, students))
print(excellent)
# [{'name': '김철수', 'score': 92}]
|
🎯 학습 목표 4: 람다 조합 패턴 익히기
map + filter 조합
1
2
3
4
5
6
7
8
9
| numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 짝수만 골라서 제곱
even_squares = list(map(lambda x: x ** 2, filter(lambda x: x % 2 == 0, numbers)))
print(even_squares) # [4, 16, 36, 64, 100]
# 5보다 큰 수만 두 배
doubled_greater = list(map(lambda x: x * 2, filter(lambda x: x > 5, numbers)))
print(doubled_greater) # [12, 14, 16, 18, 20]
|
sorted + map 조합
1
2
3
4
5
6
| words = ["Python", "is", "awesome", "and", "powerful"]
# 길이순으로 정렬 후 대문자 변환
sorted_upper = list(map(lambda w: w.upper(), sorted(words, key=lambda w: len(w))))
print(sorted_upper)
# ['IS', 'AND', 'PYTHON', 'AWESOME', 'POWERFUL']
|
종합 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| students = [
{"name": "홍길동", "age": 25, "score": 85},
{"name": "김철수", "age": 22, "score": 92},
{"name": "이영희", "age": 24, "score": 78},
{"name": "박민수", "age": 23, "score": 88},
{"name": "최영희", "age": 26, "score": 95}
]
# 80점 이상 학생만 골라서, 점수순 정렬 후, 이름만 추출
top_students = list(map(
lambda s: s["name"],
sorted(
filter(lambda s: s["score"] >= 80, students),
key=lambda s: s["score"],
reverse=True
)
))
print(top_students)
# ['최영희', '김철수', '박민수', '홍길동']
|
🎯 학습 목표 5: 람다 실전 활용과 주의사항
람다가 빛나는 상황
상황 1: 정렬 키 함수
1
2
3
4
5
6
| # 파일명을 확장자별로 정렬
files = ["report.pdf", "data.csv", "image.png", "script.py", "readme.md"]
sorted_files = sorted(files, key=lambda f: f.split('.')[-1])
print(sorted_files)
# ['data.csv', 'readme.md', 'report.pdf', 'image.png', 'script.py']
|
상황 2: 데이터 변환
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # API 응답 데이터 변환
api_data = [
{"user_id": 1, "user_name": "홍길동", "user_age": 25},
{"user_id": 2, "user_name": "김철수", "user_age": 30}
]
# 키 이름 간소화
simplified = list(map(lambda d: {
"id": d["user_id"],
"name": d["user_name"],
"age": d["user_age"]
}, api_data))
print(simplified)
# [{'id': 1, 'name': '홍길동', 'age': 25}, {'id': 2, 'name': '김철수', 'age': 30}]
|
상황 3: 조건부 변환
1
2
3
4
5
6
| # 숫자 리스트를 양수/음수 표시로 변환
numbers = [-5, 3, -2, 7, 0, -1, 4]
signs = list(map(lambda x: "+" if x > 0 else ("-" if x < 0 else "0"), numbers))
print(signs)
# ['-', '+', '-', '+', '0', '-', '+']
|
람다의 한계와 주의사항
주의 1: 한 줄만 가능
1
2
3
4
5
6
7
8
9
10
11
| # ❌ 여러 줄 불가능
# result = lambda x:
# if x > 0:
# return x
# else:
# return -x
# ✅ 조건 표현식 사용
abs_value = lambda x: x if x > 0 else -x
print(abs_value(-5)) # 5
print(abs_value(3)) # 3
|
주의 2: 복잡한 로직은 def 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # ❌ 람다로 하기에는 너무 복잡
# grade = lambda score: "A" if score >= 90 else "B" if score >= 80 else "C" if score >= 70 else "F"
# ✅ def 함수 사용
def get_grade(score):
if score >= 90:
return "A"
elif score >= 80:
return "B"
elif score >= 70:
return "C"
else:
return "F"
print(get_grade(85)) # B
|
주의 3: 가독성 우선
1
2
3
4
5
6
7
8
| # ❌ 읽기 어려운 람다
result = list(map(lambda x: x ** 2 if x % 2 == 0 else x ** 3, filter(lambda x: x > 5, range(20))))
# ✅ 명확한 코드
numbers = range(20)
greater_than_5 = filter(lambda x: x > 5, numbers)
transformed = map(lambda x: x ** 2 if x % 2 == 0 else x ** 3, greater_than_5)
result = list(transformed)
|
주의 4: 디버깅 어려움
1
2
3
4
5
6
7
8
9
10
| # 람다는 이름이 없어서 에러 추적 어려움
# def 함수는 함수 이름으로 에러 위치 파악 가능
# ✅ 중요한 로직은 def 사용
def calculate_discount(price):
return price * 0.9
# ✅ 간단한 일회성 연산은 람다 OK
prices = [100, 200, 300]
discounted = list(map(lambda p: p * 0.9, prices))
|
트러블슈팅
문제 1: 람다 안에서 변수 수정
증상:
1
2
| # ❌ 람다는 표현식만 가능, 할당문 불가
# result = lambda x: count += 1 # SyntaxError
|
해결:
1
2
3
4
5
6
| # ✅ def 함수 사용
count = 0
def increment():
global count
count += 1
return count
|
문제 2: 람다 참조 오류
증상:
1
2
3
4
5
6
7
| # ❌ 예상과 다른 결과
functions = []
for i in range(3):
functions.append(lambda: i)
# 모두 2를 반환 (마지막 i 값)
print([f() for f in functions]) # [2, 2, 2]
|
해결:
1
2
3
4
5
6
| # ✅ 기본값으로 값 캡처
functions = []
for i in range(3):
functions.append(lambda x=i: x)
print([f() for f in functions]) # [0, 1, 2]
|
🧪 연습 문제
문제 1: 온도 변환기
람다 함수를 사용해서 섭씨를 화씨로 변환하세요. (화씨 = 섭씨 * 9/5 + 32)
💡 힌트
1
| celsius_to_fahrenheit = lambda c: # 공식 적용
|
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # 람다 함수 정의
celsius_to_fahrenheit = lambda c: c * 9/5 + 32
# 테스트
celsius_temps = [0, 10, 20, 30, 37, 100]
fahrenheit_temps = list(map(celsius_to_fahrenheit, celsius_temps))
for c, f in zip(celsius_temps, fahrenheit_temps):
print(f"{c}°C = {f:.1f}°F")
# 0°C = 32.0°F
# 10°C = 50.0°F
# 20°C = 68.0°F
# 30°C = 86.0°F
# 37°C = 98.6°F
# 100°C = 212.0°F
|
문제 2: 이름 필터링
다음 리스트에서 성이 “김”인 사람만 필터링하세요.
1
| names = ["홍길동", "김철수", "이영희", "김민지", "박민수", "김영수"]
|
💡 힌트
filter()와 startswith() 사용
1
| kim_names = list(filter(lambda name: name.startswith("김"), names))
|
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
| names = ["홍길동", "김철수", "이영희", "김민지", "박민수", "김영수"]
# 성이 "김"인 사람만
kim_names = list(filter(lambda name: name.startswith("김"), names))
print(kim_names)
# ['김철수', '김민지', '김영수']
# 이름 길이가 3글자인 사람
three_char_names = list(filter(lambda name: len(name) == 3, names))
print(three_char_names)
# ['홍길동', '김철수', '이영희', '박민수', '김영수']
|
문제 3: 성적 등급 계산
학생 리스트를 점수순으로 정렬하고, 90점 이상만 이름을 추출하세요.
1
2
3
4
5
6
7
| students = [
{"name": "홍길동", "score": 85},
{"name": "김철수", "score": 92},
{"name": "이영희", "score": 78},
{"name": "박민수", "score": 95},
{"name": "최영희", "score": 88}
]
|
💡 힌트
filter()로 90점 이상만 sorted()로 점수순 정렬 map()으로 이름만 추출
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| students = [
{"name": "홍길동", "score": 85},
{"name": "김철수", "score": 92},
{"name": "이영희", "score": 78},
{"name": "박민수", "score": 95},
{"name": "최영희", "score": 88}
]
# 90점 이상 학생 필터링
excellent_students = filter(lambda s: s["score"] >= 90, students)
# 점수순 정렬 (내림차순)
sorted_students = sorted(excellent_students, key=lambda s: s["score"], reverse=True)
# 이름만 추출
names = list(map(lambda s: s["name"], sorted_students))
print(names)
# ['박민수', '김철수']
# 한 줄로 작성
names_oneline = list(map(
lambda s: s["name"],
sorted(
filter(lambda s: s["score"] >= 90, students),
key=lambda s: s["score"],
reverse=True
)
))
print(names_oneline)
# ['박민수', '김철수']
|
❓ 자주 묻는 질문 (FAQ)
Q1. 람다 함수와 일반 함수(def)는 언제 구분해서 사용하나요?
람다 함수를 사용할 때:
sorted(), map(), filter() 등의 고차 함수에 간단한 로직을 전달할 때 - 일회용으로 사용되는 간단한 함수가 필요할 때
- 함수 로직이 한 줄로 표현 가능할 때
1
2
3
4
5
6
7
| # 람다가 적절한 경우
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))
# 정렬 키로 사용
students = [{"name": "김철수", "score": 85}, {"name": "이영희", "score": 92}]
sorted_students = sorted(students, key=lambda s: s["score"])
|
일반 함수(def)를 사용할 때:
- 함수에 이름을 부여하고 여러 곳에서 재사용할 때
- 복잡한 로직이 필요할 때
- 여러 줄의 코드가 필요할 때
- 디버깅이 중요한 경우
1
2
3
4
5
6
7
8
9
10
| # def가 적절한 경우
def validate_email(email):
"""이메일 형식을 검증하는 함수"""
if not email:
return False
if "@" not in email:
return False
if "." not in email.split("@")[1]:
return False
return True
|
Q2. 람다 함수 안에서 왜 변수를 수정할 수 없나요?
람다 함수는 표현식(expression)만 사용 가능하고, 문장(statement)은 사용할 수 없기 때문입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # ❌ 불가능: 할당문은 statement
# lambda x: y = x + 1 # SyntaxError
# ❌ 불가능: 여러 줄의 코드
# lambda x: (
# result = x * 2
# return result
# )
# ✅ 가능: 단일 표현식
lambda x: x * 2
# ✅ 가능: 조건 표현식 (삼항 연산자)
lambda x: "양수" if x > 0 else "음수 또는 0"
# ✅ 가능: 여러 표현식을 튜플로 반환
lambda x, y: (x + y, x - y, x * y)
|
해결 방법: 복잡한 로직이 필요하면 def 함수를 사용하세요.
1
2
3
4
5
| def complex_operation(x):
result = x * 2
if result > 100:
result = 100
return result
|
Q3. 람다 함수의 성능이 일반 함수보다 느린가요?
아니오, 람다 함수와 일반 함수의 성능은 거의 동일합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| import time
# 람다 함수
lambda_square = lambda x: x ** 2
# 일반 함수
def def_square(x):
return x ** 2
# 성능 테스트
numbers = list(range(1000000))
# 람다 테스트
start = time.time()
result1 = list(map(lambda x: x ** 2, numbers))
lambda_time = time.time() - start
# def 테스트
start = time.time()
result2 = list(map(def_square, numbers))
def_time = time.time() - start
print(f"람다: {lambda_time:.4f}초")
print(f"def: {def_time:.4f}초")
# 차이는 거의 없음 (0.001초 미만)
|
성능보다 중요한 것:
- 가독성: 코드를 읽는 사람이 이해하기 쉬운가?
- 유지보수성: 나중에 수정하기 쉬운가?
- 디버깅: 문제가 생겼을 때 찾기 쉬운가?
Q4. 람다 함수를 변수에 할당하면 안 되나요?
PEP 8 스타일 가이드에서는 권장하지 않습니다.
1
2
3
4
5
6
7
8
9
10
| # ❌ PEP 8 위반: 람다를 변수에 할당
square = lambda x: x ** 2
add = lambda x, y: x + y
# ✅ 권장: def 함수 사용
def square(x):
return x ** 2
def add(x, y):
return x + y
|
이유:
- 디버깅 어려움: 에러 메시지에서 함수 이름이
<lambda>로 표시됨 - 가독성 저하: 함수의 목적이 명확하지 않음
- 문서화 불가: docstring을 추가할 수 없음
1
2
3
4
5
6
7
| # 람다의 디버깅 문제
square = lambda x: x / 0 # 일부러 에러 발생
try:
square(5)
except Exception as e:
print(e) # division by zero
# 추적 정보에서 함수 이름이 <lambda>로 나타남
|
람다는 일회용으로 사용하세요:
1
2
3
4
5
6
| # ✅ 좋은 예: 일회용으로 사용
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))
# ✅ 좋은 예: 정렬 키로 사용
students.sort(key=lambda s: s["score"])
|
Q5. 람다 함수 안에서 람다 함수를 사용할 수 있나요?
네, 가능하지만 가독성이 매우 떨어지므로 권장하지 않습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # ❌ 중첩 람다 (읽기 어려움)
nested = lambda x: (lambda y: x + y)
result = nested(10)(5) # 15
# ✅ def 함수로 명확하게
def outer(x):
def inner(y):
return x + y
return inner
result = outer(10)(5) # 15
# 또는 클로저 패턴
def make_adder(x):
"""x를 더하는 함수를 반환"""
return lambda y: x + y
add_10 = make_adder(10)
print(add_10(5)) # 15
print(add_10(20)) # 30
|
중첩 람다가 허용되는 경우:
- 함수형 프로그래밍 패턴에서 간단한 커링(currying)을 구현할 때
- 하지만 여전히
def 함수가 더 명확함
Q6. map()과 filter() 대신 리스트 컴프리헨션을 사용해도 되나요?
네, 대부분의 경우 리스트 컴프리헨션이 더 파이썬스럽고 가독성이 좋습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| numbers = [1, 2, 3, 4, 5]
# map() 사용
squared_map = list(map(lambda x: x ** 2, numbers))
# 리스트 컴프리헨션 (더 명확함)
squared_comp = [x ** 2 for x in numbers]
# filter() 사용
even_filter = list(filter(lambda x: x % 2 == 0, numbers))
# 리스트 컴프리헨션 (더 명확함)
even_comp = [x for x in numbers if x % 2 == 0]
# map() + filter() 조합
result_map = list(map(lambda x: x ** 2, filter(lambda x: x % 2 == 0, numbers)))
# 리스트 컴프리헨션 (훨씬 읽기 쉬움)
result_comp = [x ** 2 for x in numbers if x % 2 == 0]
|
언제 map()/filter()를 사용하나요?
- 이미 정의된 함수를 사용할 때:
1
2
3
4
5
6
| # map()이 더 간결함
words = ["hello", "world", "python"]
upper_words = list(map(str.upper, words))
# 컴프리헨션
upper_words = [word.upper() for word in words]
|
- 함수형 프로그래밍 스타일을 선호할 때
- lazy evaluation이 필요할 때 (리스트로 변환하지 않고 이터레이터로 사용)
PEP 8 권장사항: 대부분의 경우 리스트 컴프리헨션이 더 파이썬스럽습니다.
Q7. 람다 함수에서 여러 개의 값을 반환할 수 있나요?
네, 튜플을 사용하면 여러 값을 반환할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| # 두 수의 합과 곱을 동시에 반환
calc = lambda x, y: (x + y, x * y)
sum_result, mul_result = calc(3, 4)
print(f"합: {sum_result}, 곱: {mul_result}") # 합: 7, 곱: 12
# 실전 예제: 학생 정보에서 이름과 등급 추출
students = [
{"name": "김철수", "score": 92},
{"name": "이영희", "score": 78},
{"name": "박민수", "score": 95}
]
# 이름과 등급을 튜플로 반환
name_grade = list(map(
lambda s: (s["name"], "A" if s["score"] >= 90 else "B"),
students
))
print(name_grade)
# [('김철수', 'A'), ('이영희', 'B'), ('박민수', 'A')]
# 딕셔너리로 반환하는 것도 가능
student_info = list(map(
lambda s: {"name": s["name"], "grade": "A" if s["score"] >= 90 else "B"},
students
))
|
주의사항: 반환값이 복잡해지면 def 함수를 사용하는 것이 좋습니다.
Q8. 람다 함수는 재귀적으로 호출할 수 있나요?
이론적으로는 가능하지만 실용적이지 않고 권장하지 않습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| # ❌ 람다 재귀 (가능하지만 복잡함)
factorial = lambda n: 1 if n == 0 else n * factorial(n - 1)
print(factorial(5)) # 120
# ⚠️ 문제: factorial이 정의되기 전에 참조됨 (순환 참조)
# ✅ Y-combinator를 사용한 람다 재귀 (매우 복잡)
Y = lambda f: (lambda x: f(lambda *args: x(x)(*args)))(lambda x: f(lambda *args: x(x)(*args)))
factorial_y = Y(lambda f: lambda n: 1 if n == 0 else n * f(n - 1))
print(factorial_y(5)) # 120
# ✅✅ 권장: def 함수 사용 (명확하고 간단함)
def factorial_def(n):
"""팩토리얼을 계산하는 재귀 함수"""
if n == 0:
return 1
return n * factorial_def(n - 1)
print(factorial_def(5)) # 120
|
결론: 재귀가 필요하면 def 함수를 사용하세요. 람다 재귀는 학술적 흥미는 있지만 실무에서는 사용하지 않습니다.
💡 실전 팁 & 주의사항
1. 람다는 변수에 할당하지 말것 (PEP 8)
1
2
3
4
5
6
| # ❌ 나쁜 예: 람다를 변수에 할당
square = lambda x: x ** 2
# ✅ 좋은 예: def 함수 사용
def square(x):
return x ** 2
|
이유: PEP 8 스타일 가이드에서 람다를 변수에 할당하는 것을 권장하지 않습니다. 함수 이름이 필요하다면 def를 사용하세요.
2. 가독성이 우선
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # ❌ 읽기 어려운 중첩 람다
result = list(map(lambda x: x ** 2 if x % 2 == 0 else x ** 3,
filter(lambda x: x > 5, range(20))))
# ✅ 명확한 def 함수
def transform(x):
return x ** 2 if x % 2 == 0 else x ** 3
numbers = [x for x in range(20) if x > 5]
result = list(map(transform, numbers))
# ✅ 또는 리스트 컴프리헨션 (더 Pythonic)
result = [x ** 2 if x % 2 == 0 else x ** 3
for x in range(20) if x > 5]
|
이유: 코드를 읽는 사람(미래의 나 포함)을 위해 명확한 코드가 더 중요합니다.
3. 리스트 컴프리헨션을 고려하기
1
2
3
4
5
6
7
8
9
10
11
12
13
| numbers = [1, 2, 3, 4, 5]
# ❌ map + lambda
squares = list(map(lambda x: x ** 2, numbers))
# ✅ 리스트 컴프리헨션 (더 Pythonic)
squares = [x ** 2 for x in numbers]
# ❌ filter + lambda
evens = list(filter(lambda x: x % 2 == 0, numbers))
# ✅ 리스트 컴프리헨션
evens = [x for x in numbers if x % 2 == 0]
|
이유: 대부분의 경우 리스트 컴프리헨션이 더 읽기 쉽고 Python다운 코드입니다.
4. 람다가 빛나는 상황
1
2
3
4
5
6
7
8
9
| # ✅ sorted의 key 함수로 사용 (매우 적절!)
students = [("홍길동", 85), ("김철수", 92), ("이영희", 78)]
sorted_students = sorted(students, key=lambda s: s[1])
# ✅ max/min의 key 함수로 사용
youngest = min(students, key=lambda s: s[1])
# ✅ 콜백 함수로 사용 (GUI 프로그래밍)
button.on_click(lambda: print("버튼 클릭!"))
|
이유: 한 번만 사용할 간단한 키 함수나 콜백에는 람다가 완벽합니다.
5. 람다의 한계 인정하기
1
2
3
4
5
6
7
8
9
10
11
12
13
| # ❌ 람다로는 불가능한 작업들
# - 여러 문장 (Statements)
# - 할당문 (Assignments)
# - 복잡한 로직
# ✅ def 함수 사용
def complex_function(x):
if x < 0:
return 0
elif x > 100:
return 100
else:
return x
|
이유: 람다는 단일 표현식만 가능합니다. 복잡한 로직에는 def를 사용하세요.
6. type() 으로 확인
1
2
3
4
| f = lambda x: x + 1
print(type(f)) # <class 'function'>
print(f.__name__) # <lambda>
|
이유: 람다도 함수입니다! 다만 이름이 <lambda>로 표시되어 디버깅이 어렵습니다.
7. 디버깅 어려움 인식하기
1
2
3
4
5
6
7
8
9
10
11
12
| # ❌ 람다에서 에러 발생 시 추적이 어려움
data = [1, 2, "three", 4, 5]
result = list(map(lambda x: x * 2, data))
# TypeError: can't multiply sequence by non-int of type 'str'
# 에러 메시지에서 함수 이름이 <lambda>로만 표시됨
# ✅ def 함수는 함수 이름으로 에러 위치 파악 가능
def multiply_by_2(x):
return x * 2
result = list(map(multiply_by_2, data))
# TypeError in multiply_by_2() - 명확한 위치 파악
|
팁: 디버깅이 중요한 작업에는 람다 대신 def 함수를 사용하세요.
8. 람다 체이닝 주의
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # ❌ 람다 체이닝 (읽기 매우 어려움)
process = lambda data: list(map(
lambda x: x ** 2,
filter(lambda x: x % 2 == 0,
map(lambda x: x + 1, data))
))
# ✅ 단계별 명확한 처리
def process_data(data):
"""데이터 처리 파이프라인"""
# 1단계: 1 더하기
step1 = [x + 1 for x in data]
# 2단계: 짝수만 필터링
step2 = [x for x in step1 if x % 2 == 0]
# 3단계: 제곱
step3 = [x ** 2 for x in step2]
return step3
# ✅ 또는 리스트 컴프리헨션 (한 줄로 명확하게)
result = [x ** 2 for x in data if (x + 1) % 2 == 0]
|
팁: 2개 이상의 람다가 중첩되면 반드시 다른 방법을 고려하세요.
9. 기본 인자 활용하기
1
2
3
4
5
6
7
8
9
10
11
| # 기본 인자를 활용한 람다
multiply = lambda x, y=2: x * y
print(multiply(5)) # 10 (y=2 기본값)
print(multiply(5, 3)) # 15 (y=3 지정)
# 실전 예제: 할인율 계산
apply_discount = lambda price, discount=0.1: price * (1 - discount)
print(apply_discount(10000)) # 9000원 (10% 할인)
print(apply_discount(10000, 0.2)) # 8000원 (20% 할인)
|
팁: 기본 인자를 사용하면 람다의 유연성이 높아집니다.
10. 튜플 언패킹 활용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # 튜플을 받는 람다
points = [(1, 2), (3, 1), (5, 4), (2, 3)]
# ✅ 튜플 언패킹으로 명확하게
sorted_by_y = sorted(points, key=lambda p: p[1])
# ✅ 튜플 언패킹 활용 (더 명확함)
sorted_by_sum = sorted(points, key=lambda xy: xy[0] + xy[1])
# 실전 예제: 학생 정보 정렬
students = [
("김철수", 85, "서울"),
("이영희", 92, "부산"),
("박민수", 78, "대구")
]
# 이름, 점수, 지역을 활용한 다중 정렬
sorted_students = sorted(
students,
key=lambda student: (student[2], -student[1]) # 지역 오름차순, 점수 내림차순
)
|
팁: 인덱스보다는 명확한 변수명을 사용하면 가독성이 높아집니다.
11. None 반환 주의
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # ❌ 람다가 None을 반환하는 경우 (의도하지 않은 동작)
numbers = [1, 2, 3, 4, 5]
result = list(map(lambda x: print(x), numbers))
print(result) # [None, None, None, None, None]
# ✅ 값을 반환하도록 수정
result = list(map(lambda x: x * 2, numbers))
print(result) # [2, 4, 6, 8, 10]
# 실전 예제: 부작용(side effect) 주의
data = []
list(map(lambda x: data.append(x * 2), range(5)))
# 동작은 하지만 권장하지 않음 (부작용 발생)
# ✅ 명확한 for 문 사용
data = []
for x in range(5):
data.append(x * 2)
|
팁: 람다는 값을 반환하는 순수 함수로 사용하세요.
12. 조건부 표현식 활용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| # 삼항 연산자를 활용한 람다
is_adult = lambda age: "성인" if age >= 19 else "미성년자"
print(is_adult(25)) # 성인
print(is_adult(17)) # 미성년자
# 실전 예제: 등급 분류
grade = lambda score: "A" if score >= 90 else "B" if score >= 80 else "C"
students = [85, 92, 78, 95, 88]
grades = list(map(grade, students))
print(grades) # ['B', 'A', 'C', 'A', 'B']
# 복잡한 조건은 def 사용 권장
def calculate_grade(score):
"""점수에 따른 등급 계산"""
if score >= 90:
return "A"
elif score >= 80:
return "B"
elif score >= 70:
return "C"
else:
return "F"
|
팁: 조건이 2개 이상이면 람다보다 def 함수가 더 명확합니다.
13. 클로저와 함께 사용하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # 람다를 반환하는 함수 (클로저 패턴)
def make_multiplier(n):
"""n을 곱하는 함수를 생성"""
return lambda x: x * n
multiply_by_3 = make_multiplier(3)
multiply_by_10 = make_multiplier(10)
print(multiply_by_3(5)) # 15
print(multiply_by_10(5)) # 50
# 실전 예제: 세금 계산기
def make_tax_calculator(rate):
"""세율에 따른 세금 계산 함수 생성"""
return lambda amount: amount * rate
income_tax = make_tax_calculator(0.1) # 소득세 10%
vat = make_tax_calculator(0.1) # 부가세 10%
print(f"소득세: {income_tax(100000):,}원")
print(f"부가세: {vat(100000):,}원")
|
팁: 클로저 패턴에서 람다는 매우 유용합니다.
14. operator 모듈 활용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| from operator import add, mul, itemgetter, attrgetter
numbers = [1, 2, 3, 4, 5]
# ❌ 람다 사용
from functools import reduce
sum_result = reduce(lambda x, y: x + y, numbers)
# ✅ operator 모듈 사용 (더 효율적)
sum_result = reduce(add, numbers)
# 딕셔너리 정렬에 활용
students = [
{"name": "김철수", "score": 85},
{"name": "이영희", "score": 92},
]
# ❌ 람다
sorted_students = sorted(students, key=lambda s: s["score"])
# ✅ itemgetter (더 빠르고 명확)
sorted_students = sorted(students, key=itemgetter("score"))
|
팁: operator 모듈의 함수들은 람다보다 성능이 좋습니다.
15. docstring 부재 인식
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| # ❌ 람다는 docstring을 가질 수 없음
square = lambda x: x ** 2
# square.__doc__는 None
# ✅ def 함수는 docstring으로 문서화 가능
def square(x):
"""
주어진 수의 제곱을 계산합니다.
Args:
x: 제곱할 숫자
Returns:
x의 제곱값
Examples:
>>> square(5)
25
"""
return x ** 2
print(square.__doc__) # docstring 출력
help(square) # 도움말 확인 가능
|
팁: 함수 문서화가 필요하면 반드시 def를 사용하세요.
📝 오늘 배운 내용 정리
- 람다 함수:
lambda 매개변수: 표현식 형태의 익명 함수 - sorted():
key=lambda 로 정렬 기준 지정 - map(): 모든 요소에 함수 적용
- filter(): 조건에 맞는 요소만 선택
- 사용 시기: 간단한 일회성 함수, 복잡하면 def 사용
- PEP 8: 람다를 변수에 할당하지 말고 def 사용
- Pythonic: 리스트 컴프리헨션을 먼저 고려
🎯 실습 과제
과제: 상품 관리 시스템
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
| products = [
{"name": "노트북", "price": 1200000, "category": "전자제품", "stock": 5},
{"name": "마우스", "price": 30000, "category": "전자제품", "stock": 20},
{"name": "키보드", "price": 80000, "category": "전자제품", "stock": 15},
{"name": "책상", "price": 150000, "category": "가구", "stock": 8},
{"name": "의자", "price": 200000, "category": "가구", "stock": 12}
]
# 1. 100,000원 이상 제품만 필터링
expensive = list(filter(lambda p: p["price"] >= 100000, products))
print("고가 제품:")
for p in expensive:
print(f"- {p['name']}: {p['price']:,}원")
# 2. 가격순 정렬 (내림차순)
sorted_by_price = sorted(products, key=lambda p: p["price"], reverse=True)
print("\n가격순 정렬:")
for p in sorted_by_price:
print(f"- {p['name']}: {p['price']:,}원")
# 3. 카테고리별 그룹화 (딕셔너리로)
categories = {}
for product in products:
category = product["category"]
if category not in categories:
categories[category] = []
categories[category].append(product["name"])
print("\n카테고리별 상품:")
for category, items in categories.items():
print(f"{category}: {', '.join(items)}")
# 4. 재고가 10개 이상인 제품의 이름만 추출
high_stock_names = list(map(
lambda p: p["name"],
filter(lambda p: p["stock"] >= 10, products)
))
print(f"\n재고 충분 상품: {', '.join(high_stock_names)}")
# 5. 각 제품의 총 가치 (가격 * 재고) 계산
total_values = list(map(
lambda p: {"name": p["name"], "total_value": p["price"] * p["stock"]},
products
))
print("\n제품별 총 가치:")
for item in sorted(total_values, key=lambda x: x["total_value"], reverse=True):
print(f"- {item['name']}: {item['total_value']:,}원")
|
🔗 관련 자료
📚 이전 학습
Day 27: *args와 **kwargs ⭐
어제는 *args와 **kwargs로 가변 인자를 다루는 방법을 배웠습니다!
📚 다음 학습
Day 29: 스코프와 전역 변수 ⭐⭐⭐
내일은 변수의 유효 범위와 전역/지역 변수를 배웁니다!
“늦었다고 생각할 때가 가장 빠른 시기입니다!” 🚀
| Day 28/100 | Phase 3: 제어문과 함수 | #100DaysOfPython |