포스트

[이제와서 시작하는 Python 마스터하기 #4] 함수와 람다 완벽 가이드

[이제와서 시작하는 Python 마스터하기 #4] 함수와 람다 완벽 가이드

image: path: /assets/img/posts/python/python-mastery-04-functions-lambda.png alt: Python 함수와 람다

🎮 5분만에 배우는 함수의 마법

💸 실전 예제: 할인 계산기 만들기

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
48
49
50
51
def calculate_discount():
    """쇼핑몰 할인 계산 프로그램"""

    # 상품 가격
    items = [
        {"name": "노트북", "price": 1200000, "qty": 1},
        {"name": "마우스", "price": 35000, "qty": 2},
        {"name": "키보드", "price": 89000, "qty": 1}
    ]

    def get_total(items):
        """총액 계산 함수"""
        return sum(item["price"] * item["qty"] for item in items)

    def apply_discount(amount, rate):
        """할인 적용 함수"""
        return amount * (1 - rate)

    def get_membership_discount(level):
        """회원 등급별 할인율"""
        discounts = {
            "VIP": 0.20,     # 20% 할인
            "Gold": 0.15,    # 15% 할인
            "Silver": 0.10,  # 10% 할인
            "Bronze": 0.05   # 5% 할인
        }
        return discounts.get(level, 0)

    # 계산 실행
    total = get_total(items)
    membership = "Gold"
    discount_rate = get_membership_discount(membership)
    final_price = apply_discount(total, discount_rate)
    saved = total - final_price

    # 영수증 출력
    print("🛒 온라인 쇼핑몰 영수증")
    print("="*40)
    for item in items:
        subtotal = item["price"] * item["qty"]
        print(f"{item['name']}: {item['price']:,}원 x {item['qty']}개 = {subtotal:,}")
    print("="*40)
    print(f"총액: {total:,}")
    print(f"회원등급: {membership} ({int(discount_rate*100)}% 할인)")
    print(f"할인금액: -{saved:,.0f}")
    print(f"결제금액: {final_price:,.0f}")

    return final_price

# 실행
# calculate_discount()

🎯 함수란 무엇인가?

함수는 특정 작업을 수행하는 코드 블록으로, 필요할 때마다 호출하여 재사용할 수 있습니다. 코드의 중복을 줄이고, 프로그램을 모듈화하여 유지보수를 쉽게 만들어줍니다.

[!TIP] 초보자를 위한 팁: 함수는 요리 레시피와 같아요!

  • 매개변수(Input): 요리 재료 (계란, 소금)
  • 함수 내부(Process): 요리 과정 (볶기, 끓이기)
  • 반환값(Output): 완성된 요리 (계란말이)

레시피 하나만 잘 만들어두면, 재료만 바꿔서 언제든 맛있는 요리를 만들 수 있죠? 함수도 똑같습니다!

graph LR
    A[입력<br/>Input] --> B[함수<br/>Function]
    B --> C[처리<br/>Process]
    C --> D[출력<br/>Output]
    
    E[매개변수<br/>Parameters] --> B
    B --> F[반환값<br/>Return Value]

📝 함수의 기본 구조

함수 정의와 호출

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 기본 함수 정의
def greet():
    """인사하는 함수"""  # 독스트링 (함수 설명)
    print("안녕하세요!")

# 함수 호출
greet()  # 안녕하세요!

# 매개변수가 있는 함수
def greet_with_name(name):
    """이름을 받아 인사하는 함수"""
    print(f"안녕하세요, {name}님!")

greet_with_name("김파이썬")  # 안녕하세요, 김파이썬님!

# 반환값이 있는 함수
def add(a, b):
    """두 수를 더하는 함수"""
    return a + b

result = add(3, 5)
print(result)  # 8

매개변수와 인자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 위치 인자 (Positional Arguments)
def introduce(name, age, city):
    print(f"{city}에 사는 {age}{name}입니다.")

introduce("홍길동", 25, "서울")  # 순서 중요!

# 키워드 인자 (Keyword Arguments)
introduce(city="부산", name="김철수", age=30)  # 순서 무관

# 기본값 매개변수 (Default Parameters)
def greet(name, greeting="안녕하세요"):
    print(f"{greeting}, {name}님!")

greet("박영희")  # 안녕하세요, 박영희님!
greet("이민수", "반갑습니다")  # 반갑습니다, 이민수님!

# 주의: 기본값 매개변수는 일반 매개변수 뒤에 위치
# def wrong(greeting="안녕", name):  # SyntaxError!
#     pass

가변 인자 (*args, **kwargs)

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
# *args: 가변 위치 인자 (튜플로 수집)
def sum_all(*numbers):
    """여러 개의 숫자를 모두 더하는 함수"""
    total = 0
    for num in numbers:
        total += num
    return total

print(sum_all(1, 2, 3))  # 6
print(sum_all(1, 2, 3, 4, 5))  # 15

# **kwargs: 가변 키워드 인자 (딕셔너리로 수집)
def print_info(**info):
    """키워드 인자를 출력하는 함수"""
    for key, value in info.items():
        print(f"{key}: {value}")

print_info(name="김파이썬", age=25, city="서울")

# *args와 **kwargs 함께 사용
def flexible_function(required, *args, **kwargs):
    print(f"필수 인자: {required}")
    print(f"추가 위치 인자: {args}")
    print(f"키워드 인자: {kwargs}")

flexible_function("필수", 1, 2, 3, name="테스트", debug=True)

언패킹 (Unpacking)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 리스트/튜플 언패킹 (*)
numbers = [1, 2, 3, 4, 5]
print(sum_all(*numbers))  # 리스트를 개별 인자로 전달

# 딕셔너리 언패킹 (**)
info = {"name": "박코딩", "age": 28, "city": "대전"}
print_info(**info)  # 딕셔너리를 키워드 인자로 전달

# 함수 정의와 호출에서 모두 사용 가능
def coordinate(x, y, z):
    return f"좌표: ({x}, {y}, {z})"

point = (10, 20, 30)
print(coordinate(*point))  # 좌표: (10, 20, 30)

🔄 함수의 반환값

단일 반환값과 다중 반환값

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 단일 반환값
def square(n):
    return n ** 2

# 다중 반환값 (튜플로 반환)
def min_max(numbers):
    return min(numbers), max(numbers)

minimum, maximum = min_max([3, 1, 4, 1, 5, 9])
print(f"최소: {minimum}, 최대: {maximum}")  # 최소: 1, 최대: 9

# 조건부 반환
def divide(a, b):
    if b == 0:
        return None  # 또는 raise ValueError("0으로 나눌 수 없습니다")
    return a / b

# return 없으면 None 반환
def no_return():
    print("반환값이 없는 함수")

result = no_return()
print(result)  # None

[!WARNING] print()와 return의 차이

초보자들이 가장 많이 헷갈리는 부분입니다!

  • print(): 화면에 보여주기만 하고, 값은 사라집니다. (눈으로 확인용)
  • return: 값을 함수 밖으로 던져줍니다. 다른 변수에 저장하거나 계산에 쓸 수 있습니다. (재사용용)

함수가 값을 “뱉어내게” 하려면 반드시 return을 써야 합니다!

🌟 람다 함수 (Lambda Functions)

📊 실무 예제: 데이터 분석 with Lambda

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
48
49
50
51
52
53
def employee_data_analysis():
    """람다를 활용한 직원 데이터 분석"""

    employees = [
        {"name": "김철수", "dept": "개발팀", "salary": 4500, "years": 3},
        {"name": "이영희", "dept": "마케팅", "salary": 3800, "years": 2},
        {"name": "박민수", "dept": "개발팀", "salary": 5200, "years": 5},
        {"name": "정수진", "dept": "인사팀", "salary": 3500, "years": 1},
        {"name": "최동욱", "dept": "개발팀", "salary": 6000, "years": 7},
        {"name": "강미나", "dept": "마케팅", "salary": 4200, "years": 4}
    ]

    print("🏢 직원 데이터 분석 시스템\n")

    # 1. 연봉 5000만원 이상 직원 찾기
    high_salary = list(filter(lambda e: e["salary"] >= 5000, employees))
    print("💰 고연봉자 (5000만원 이상):")
    for emp in high_salary:
        print(f"  - {emp['name']}: {emp['salary']}만원")

    # 2. 부서별 평균 연봉 계산
    depts = set(e["dept"] for e in employees)
    print("\n📊 부서별 평균 연봉:")
    for dept in depts:
        dept_employees = list(filter(lambda e: e["dept"] == dept, employees))
        avg_salary = sum(e["salary"] for e in dept_employees) / len(dept_employees)
        print(f"  - {dept}: {avg_salary:,.0f}만원")

    # 3. 연차별 정렬 (내림차순)
    sorted_by_years = sorted(employees, key=lambda e: e["years"], reverse=True)
    print("\n👔 경력 순위:")
    for i, emp in enumerate(sorted_by_years[:3], 1):
        print(f"  {i}위: {emp['name']} ({emp['years']}년차)")

    # 4. 연봉 인상 시뮬레이션 (연차 * 2%)
    with_raise = list(map(
        lambda e: {**e, "new_salary": e["salary"] * (1 + e["years"] * 0.02)},
        employees
    ))
    print("\n📈 연봉 인상 시뮬레이션 (연차당 2%):")
    for emp in with_raise:
        increase = emp["new_salary"] - emp["salary"]
        print(f"  - {emp['name']}: {emp['salary']}만원 → {emp['new_salary']:.0f}만원 (+{increase:.0f}만원)")

    # 5. 개발팀 총 인건비
    dev_team_cost = sum(
        map(lambda e: e["salary"],
            filter(lambda e: e["dept"] == "개발팀", employees))
    )
    print(f"\n💻 개발팀 총 인건비: {dev_team_cost:,}만원/년")

# 실행
# employee_data_analysis()

람다 함수는 이름 없는 익명 함수로, 간단한 함수를 한 줄로 작성할 때 사용합니다.

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
# 기본 람다 함수
square = lambda x: x ** 2
print(square(5))  # 25

# 일반 함수와 비교
def square_func(x):
    return x ** 2

# 여러 매개변수
add = lambda x, y: x + y
print(add(3, 5))  # 8

# 조건 표현식 포함
max_value = lambda x, y: x if x > y else y
print(max_value(10, 20))  # 20

# 람다 함수의 주요 용도: 고차 함수의 인자
numbers = [1, 2, 3, 4, 5]

# map() 함수와 함께
squared = list(map(lambda x: x ** 2, numbers))
print(squared)  # [1, 4, 9, 16, 25]

# filter() 함수와 함께
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)  # [2, 4]

# sorted() 함수와 함께
students = [
    {"name": "김철수", "score": 85},
    {"name": "이영희", "score": 92},
    {"name": "박민수", "score": 78}
]

# 점수로 정렬
sorted_students = sorted(students, key=lambda s: s["score"], reverse=True)
for student in sorted_students:
    print(f"{student['name']}: {student['score']}")

🎨 고급 함수 개념

중첩 함수 (Nested Functions)

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
def outer_function(x):
    """외부 함수"""
    
    def inner_function(y):
        """내부 함수 - 외부 함수의 변수에 접근 가능"""
        return x + y
    
    return inner_function

# 클로저 생성
add_five = outer_function(5)
print(add_five(3))  # 8
print(add_five(10))  # 15

# 실용적 예제: 설정 가능한 할인율 계산기
def discount_calculator(discount_rate):
    """할인율을 설정하는 계산기 생성"""
    def calculate_price(original_price):
        return original_price * (1 - discount_rate)
    return calculate_price

# 10% 할인 계산기
ten_percent_off = discount_calculator(0.1)
print(ten_percent_off(10000))  # 9000.0

# 30% 할인 계산기
thirty_percent_off = discount_calculator(0.3)
print(thirty_percent_off(10000))  # 7000.0

고차 함수 (Higher-Order Functions)

함수를 인자로 받거나 함수를 반환하는 함수입니다.

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
# 함수를 인자로 받는 고차 함수
def apply_operation(func, x, y):
    """주어진 함수를 x와 y에 적용"""
    return func(x, y)

def add(a, b):
    return a + b

def multiply(a, b):
    return a * b

print(apply_operation(add, 5, 3))       # 8
print(apply_operation(multiply, 5, 3))  # 15

# 함수를 반환하는 고차 함수
def make_multiplier(n):
    """n배 하는 함수를 반환"""
    def multiplier(x):
        return x * n
    return multiplier

double = make_multiplier(2)
triple = make_multiplier(3)

print(double(5))   # 10
print(triple(5))   # 15

# 내장 고차 함수들
from functools import reduce

numbers = [1, 2, 3, 4, 5]

# map: 각 요소에 함수 적용
doubled = list(map(lambda x: x * 2, numbers))
print(doubled)  # [2, 4, 6, 8, 10]

# filter: 조건을 만족하는 요소만 선택
odds = list(filter(lambda x: x % 2 == 1, numbers))
print(odds)  # [1, 3, 5]

# reduce: 누적 연산
total = reduce(lambda x, y: x + y, numbers)
print(total)  # 15

데코레이터 기초

데코레이터는 함수를 수정하지 않고 기능을 추가하는 방법입니다.

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
# 간단한 데코레이터
def timer_decorator(func):
    """함수 실행 시간을 측정하는 데코레이터"""
    import time
    
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} 실행 시간: {end - start:.4f}")
        return result
    
    return wrapper

# 데코레이터 사용
@timer_decorator
def slow_function():
    """시간이 걸리는 함수"""
    import time
    time.sleep(1)
    return "완료!"

result = slow_function()  # slow_function 실행 시간: 1.0XXX초
print(result)  # 완료!

# 로깅 데코레이터
def log_decorator(func):
    """함수 호출을 로깅하는 데코레이터"""
    def wrapper(*args, **kwargs):
        print(f"호출: {func.__name__}({args}, {kwargs})")
        result = func(*args, **kwargs)
        print(f"반환: {result}")
        return result
    return wrapper

@log_decorator
def add(a, b):
    return a + b

add(3, 5)
# 호출: add((3, 5), {})
# 반환: 8

💡 실전 예제

1. 계산기 프로그램 (함수 활용)

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
def calculator_program():
    """함수를 활용한 계산기 프로그램"""
    
    # 기본 연산 함수들
    def add(x, y):
        return x + y
    
    def subtract(x, y):
        return x - y
    
    def multiply(x, y):
        return x * y
    
    def divide(x, y):
        if y == 0:
            raise ValueError("0으로 나눌 수 없습니다")
        return x / y
    
    def power(x, y):
        return x ** y
    
    # 연산 딕셔너리
    operations = {
        '+': add,
        '-': subtract,
        '*': multiply,
        '/': divide,
        '**': power
    }
    
    # 계산 기록
    history = []
    
    def calculate(x, operator, y):
        """계산 수행 및 기록"""
        if operator not in operations:
            raise ValueError(f"지원하지 않는 연산자: {operator}")
        
        result = operations[operator](x, y)
        history.append(f"{x} {operator} {y} = {result}")
        return result
    
    def show_history():
        """계산 기록 표시"""
        if not history:
            print("계산 기록이 없습니다.")
        else:
            print("\n=== 계산 기록 ===")
            for i, record in enumerate(history, 1):
                print(f"{i}. {record}")
    
    # 메인 루프
    print("=== 함수형 계산기 ===")
    print("연산자: +, -, *, /, **")
    print("명령: history(기록 보기), quit(종료)")
    
    while True:
        user_input = input("\n수식 입력 (예: 5 + 3): ").strip()
        
        if user_input.lower() == 'quit':
            break
        elif user_input.lower() == 'history':
            show_history()
            continue
        
        try:
            parts = user_input.split()
            if len(parts) != 3:
                print("올바른 형식: 숫자 연산자 숫자")
                continue
            
            x = float(parts[0])
            operator = parts[1]
            y = float(parts[2])
            
            result = calculate(x, operator, y)
            print(f"결과: {result}")
            
        except ValueError as e:
            print(f"에러: {e}")
        except Exception as e:
            print(f"예상치 못한 에러: {e}")

# 실행
# calculator_program()

2. 함수형 프로그래밍 스타일 데이터 처리

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
48
49
50
51
52
53
54
55
56
57
58
def functional_data_processing():
    """함수형 프로그래밍 스타일로 데이터 처리"""
    
    # 샘플 데이터
    employees = [
        {"name": "김철수", "department": "개발", "salary": 4500, "years": 3},
        {"name": "이영희", "department": "마케팅", "salary": 3800, "years": 2},
        {"name": "박민수", "department": "개발", "salary": 5200, "years": 5},
        {"name": "최지원", "department": "인사", "salary": 3500, "years": 1},
        {"name": "정대현", "department": "개발", "salary": 4800, "years": 4},
    ]
    
    # 1. 개발 부서 직원만 필터링
    developers = list(filter(lambda e: e["department"] == "개발", employees))
    print("개발 부서 직원:")
    for dev in developers:
        print(f"  - {dev['name']}")
    
    # 2. 모든 직원의 연봉 10% 인상 계산
    def raise_salary(employee):
        return {**employee, "salary": employee["salary"] * 1.1}
    
    raised_employees = list(map(raise_salary, employees))
    print("\n10% 인상 후 연봉:")
    for emp in raised_employees:
        print(f"  - {emp['name']}: {emp['salary']:.0f}만원")
    
    # 3. 경력 3년 이상 직원의 평균 연봉
    senior_employees = filter(lambda e: e["years"] >= 3, employees)
    senior_salaries = map(lambda e: e["salary"], senior_employees)
    avg_salary = sum(senior_salaries) / len([e for e in employees if e["years"] >= 3])
    print(f"\n경력 3년 이상 평균 연봉: {avg_salary:.0f}만원")
    
    # 4. 부서별 직원 수 계산
    from collections import defaultdict
    
    def group_by_department(employees):
        groups = defaultdict(list)
        for emp in employees:
            groups[emp["department"]].append(emp)
        return dict(groups)
    
    dept_groups = group_by_department(employees)
    print("\n부서별 직원 수:")
    for dept, emps in dept_groups.items():
        print(f"  - {dept}: {len(emps)}")
    
    # 5. 커링(Currying) 패턴으로 급여 필터 생성
    def salary_filter(min_salary):
        return lambda emp: emp["salary"] >= min_salary
    
    high_earners = filter(salary_filter(4500), employees)
    print("\n연봉 4500만원 이상:")
    for emp in high_earners:
        print(f"  - {emp['name']}: {emp['salary']}만원")

# 실행
# functional_data_processing()

3. 데코레이터 활용 예제

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import functools
import time

# 캐싱 데코레이터
def memoize(func):
    """결과를 캐싱하는 데코레이터"""
    cache = {}
    
    @functools.wraps(func)
    def wrapper(*args):
        if args in cache:
            print(f"캐시에서 반환: {args}")
            return cache[args]
        result = func(*args)
        cache[args] = result
        return result
    
    wrapper.cache = cache  # 캐시 접근 가능하도록
    return wrapper

# 재시도 데코레이터
def retry(max_attempts=3, delay=1):
    """실패시 재시도하는 데코레이터"""
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_attempts - 1:
                        raise
                    print(f"시도 {attempt + 1} 실패: {e}")
                    time.sleep(delay)
            return None
        return wrapper
    return decorator

# 입력 검증 데코레이터
def validate_positive(func):
    """양수만 허용하는 데코레이터"""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        for arg in args:
            if isinstance(arg, (int, float)) and arg < 0:
                raise ValueError(f"음수는 허용되지 않습니다: {arg}")
        return func(*args, **kwargs)
    return wrapper

# 데코레이터 사용 예제
@memoize
def fibonacci(n):
    """피보나치 수열 (캐싱 적용)"""
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

@retry(max_attempts=3, delay=0.5)
def unstable_network_call():
    """불안정한 네트워크 호출 시뮬레이션"""
    import random
    if random.random() < 0.7:  # 70% 확률로 실패
        raise ConnectionError("네트워크 연결 실패")
    return "성공!"

@validate_positive
def calculate_area(width, height):
    """사각형 넓이 계산 (양수 검증)"""
    return width * height

# 테스트
print("=== 피보나치 (캐싱) ===")
print(f"fibonacci(10) = {fibonacci(10)}")
print(f"fibonacci(10) = {fibonacci(10)}")  # 캐시에서 반환

print("\n=== 불안정한 네트워크 호출 ===")
try:
    result = unstable_network_call()
    print(f"결과: {result}")
except ConnectionError as e:
    print(f"최종 실패: {e}")

print("\n=== 넓이 계산 (검증) ===")
try:
    print(f"넓이: {calculate_area(5, 3)}")
    print(f"넓이: {calculate_area(-5, 3)}")  # ValueError
except ValueError as e:
    print(f"에러: {e}")

⚠️ 초보자가 자주 하는 실수

1. 기본값 매개변수 실수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# ❌ 가변 객체를 기본값으로 사용
def add_item(item, items=[]):  # 위험!
    items.append(item)
    return items

print(add_item("apple"))   # ['apple']
print(add_item("banana"))  # ['apple', 'banana'] - 예상과 다름!

# ✅ None을 기본값으로 사용
def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

[!CAUTION] 기본값으로 리스트를 쓰지 마세요!

리스트 같은 가변 객체(Mutable Object)를 기본값으로 쓰면, 함수가 실행될 때마다 리스트가 초기화되는 게 아니라 계속 재사용됩니다. 그래서 앞서 추가한 아이템이 계속 남아있게 됩니다. 반드시 None을 사용하세요!

2. 변수 스코프 실수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# ❌ 전역 변수와 지역 변수 혼동
count = 10

def increment():
    count = count + 1  # UnboundLocalError!
    return count

# ✅ global 키워드 사용 또는 매개변수로 전달
def increment():
    global count
    count = count + 1
    return count

# 더 좋은 방법 - 매개변수와 반환값 사용
def increment(count):
    return count + 1

3. return 실수

1
2
3
4
5
6
7
8
9
10
11
12
13
# ❌ return 위치 실수
def find_first_even(numbers):
    for num in numbers:
        if num % 2 == 0:
            print(f"Found: {num}")
        return num  # 첫 번째 숫자만 반환!

# ✅ 올바른 위치
def find_first_even(numbers):
    for num in numbers:
        if num % 2 == 0:
            return num
    return None

4. 람다 함수 실수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# ❌ 복잡한 로직을 람다로 작성
result = lambda x: print(x) if x > 0 else (print("negative") if x < 0 else print("zero"))

# ✅ 복잡한 로직은 일반 함수로
def check_number(x):
    if x > 0:
        return "positive"
    elif x < 0:
        return "negative"
    else:
        return "zero"

# ❌ 람다 함수에서 반복문
# lambda x: for i in x: print(i)  # SyntaxError!

# ✅ 리스트 컴프리헨션 사용
lambda x: [print(i) for i in x]

5. 인자 전달 실수

1
2
3
4
5
6
7
8
9
# ❌ *args를 잘못 전달
def sum_numbers(*args):
    return sum(args)

numbers = [1, 2, 3, 4, 5]
# print(sum_numbers(numbers))  # TypeError!

# ✅ 언패킹 사용
print(sum_numbers(*numbers))  # 15

🎯 핵심 정리

함수 설계 Best Practices

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
# 1. 단일 책임 원칙
# 좋음: 한 가지 일만 하는 함수
def calculate_total(prices):
    return sum(prices)

def apply_discount(total, discount_rate):
    return total * (1 - discount_rate)

# 나쁨: 여러 일을 하는 함수
def calculate_and_print_with_discount(prices, discount_rate):
    total = sum(prices)
    discounted = total * (1 - discount_rate)
    print(f"Total: {total}, After discount: {discounted}")
    return discounted

# 2. 명확한 함수명과 매개변수명
def get_user_by_id(user_id):  # 좋음
    pass

def get(id):  # 나쁨 - 무엇을 가져오는지 불명확
    pass

# 3. 타입 힌트 사용 (Python 3.5+)
def greet(name: str, age: int) -> str:
    return f"{name}님은 {age}살입니다."

# 4. 독스트링 작성
def calculate_bmi(weight: float, height: float) -> float:
    """
    BMI(체질량지수)를 계산합니다.
    
    Args:
        weight: 체중 (kg)
        height: 키 (m)
    
    Returns:
        BMI 값
    
    Raises:
        ValueError: 체중이나 키가 0 이하인 경우
    """
    if weight <= 0 or height <= 0:
        raise ValueError("체중과 키는 양수여야 합니다")
    return weight / (height ** 2)

람다 함수 사용 가이드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 람다가 적합한 경우
# 1. 간단한 변환
numbers = [1, 2, 3, 4, 5]
squared = map(lambda x: x**2, numbers)

# 2. 정렬 키
students.sort(key=lambda s: s['score'])

# 3. 필터링
adults = filter(lambda p: p['age'] >= 18, people)

# 람다가 부적합한 경우
# 1. 복잡한 로직 - 일반 함수 사용
def complex_calculation(x, y, z):
    if x > 0:
        return (x + y) * z
    else:
        return (x - y) / z

# 2. 재사용이 필요한 경우
def is_even(n):
    return n % 2 == 0

even_nums = filter(is_even, numbers)  # 여러 곳에서 재사용 가능

🎓 파이썬 마스터하기 시리즈

📚 기초편 (1-7)

  1. Python 소개와 개발 환경 설정 완벽 가이드
  2. 변수, 자료형, 연산자 완벽 정리
  3. 조건문과 반복문 마스터하기
  4. 함수와 람다 완벽 가이드
  5. 리스트, 튜플, 딕셔너리 정복하기
  6. 문자열 처리와 정규표현식
  7. 파일 입출력과 예외 처리

🚀 중급편 (8-12)

  1. 클래스와 객체지향 프로그래밍
  2. 모듈과 패키지 관리
  3. 데코레이터와 제너레이터
  4. 비동기 프로그래밍 (async/await)
  5. 데이터베이스 연동하기

💼 고급편 (13-16)

  1. 웹 스크래핑과 API 활용
  2. 테스트와 디버깅 전략
  3. 성능 최적화 기법
  4. 멀티프로세싱과 병렬 처리

이전글: 조건문과 반복문 마스터하기 ⬅️ 현재글: 함수와 람다 완벽 가이드 다음글: 리스트, 튜플, 딕셔너리 정복하기 ➡️


이번 포스트에서는 코드 재사용의 핵심인 함수와 람다에 대해 알아보았습니다. 다음 포스트에서는 Python의 핵심 자료구조인 리스트, 튜플, 딕셔너리를 완벽히 정복해보겠습니다. Happy Coding! 🐍✨

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.