// education

함수와 모듈 설계

들어가며

함수는 코드 재사용성과 가독성을 높이는 핵심 요소입니다. 좋은 함수 설계는 프로젝트의 유지보수성을 크게 향상시킵니다.

함수의 기본

예제 1: 기본 함수 정의

# 매개변수 없음
def greet():
    print("안녕하세요!")

# 매개변수 있음
def greet_person(name):
    print(f"안녕하세요, {name}님!")

# 반환값 있음
def add(a, b):
    return a + b

# 함수 호출
greet()
greet_person("Alice")
result = add(5, 3)
print(f"5 + 3 = {result}")

출력:

안녕하세요!
안녕하세요, Alice님!
5 + 3 = 8

예제 2: 기본값 매개변수

def introduce(name, age=0, city="Seoul"):
    print(f"이름: {name}, 나이: {age}, 도시: {city}")

# 기본값 사용
introduce("Bob")
introduce("Charlie", 25)
introduce("David", 30, "Busan")

# 키워드 인자
introduce(name="Eve", city="Daegu")

출력:

이름: Bob, 나이: 0, 도시: Seoul
이름: Charlie, 나이: 25, 도시: Seoul
이름: David, 나이: 30, 도시: Busan
이름: Eve, 나이: 0, 도시: Daegu

예제 3: 가변 길이 인자

# *args - 위치 인자 (튜플)
def sum_all(*numbers):
    return sum(numbers)

print(f"sum_all(1, 2, 3) = {sum_all(1, 2, 3)}")
print(f"sum_all(10, 20) = {sum_all(10, 20)}")

# **kwargs - 키워드 인자 (딕셔너리)
def print_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print("\nprint_info 결과:")
print_info(name="Alice", age=25, job="Engineer")

출력:

sum_all(1, 2, 3) = 6
sum_all(10, 20) = 30

print_info 결과:
name: Alice
age: 25
job: Engineer

함수의 고급 개념

예제 4: 다중 반환값

def divide_and_remainder(a, b):
    quotient = a // b
    remainder = a % b
    return quotient, remainder

# 튜플로 반환
q, r = divide_and_remainder(17, 5)
print(f"17 ÷ 5 = {q} 나머지 {r}")

# 전체 튜플 수신
result = divide_and_remainder(20, 3)
print(f"결과: {result}")

출력:

17 ÷ 5 = 3 나머지 2
결과: (6, 2)

예제 5: 함수 문서화 (Docstring)

def calculate_bmi(weight, height):
    """
    체질량 지수(BMI)를 계산합니다.
    
    Args:
        weight (float): 체중 (kg)
        height (float): 신장 (m)
    
    Returns:
        float: 체질량 지수
    
    Example:
        >>> calculate_bmi(70, 1.75)
        22.857142857142858
    """
    return weight / (height ** 2)

# 문서 확인
print(calculate_bmi.__doc__)
print(f"\nBMI: {calculate_bmi(70, 1.75):.2f}")

출력:

계산합니다.
    
    Args:
        weight (float): 체중 (kg)
        height (float): 신장 (m)
    
    Returns:
        float: 체질량 지수
    
    Example:
        >>> calculate_bmi(70, 1.75)
        22.857142857142858

BMI: 22.86

예제 6: 람다 함수

# 람다 함수 정의
square = lambda x: x ** 2
add = lambda x, y: x + y

print(f"제곱(5): {square(5)}")
print(f"합계(3, 7): {add(3, 7)}")

# map과 함께 사용
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))
print(f"제곱된 숫자들: {squared}")

# filter와 함께 사용
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(f"짝수: {evens}")

출력:

제곱(5): 25
합계(3, 7): 10
제곱된 숫자들: [1, 4, 9, 16, 25]
짝수: [2, 4]

범위와 전역 변수

예제 7: 로컬 vs 글로벌 변수

global_var = "전역"

def test_scope():
    local_var = "로컬"
    print(f"함수 내부 - 로컬: {local_var}")
    print(f"함수 내부 - 전역: {global_var}")

test_scope()

print(f"함수 외부 - 전역: {global_var}")
# print(f"함수 외부 - 로컬: {local_var}")  # NameError!

def modify_global():
    global global_var
    global_var = "수정된 전역"

modify_global()
print(f"수정 후: {global_var}")

출력:

함수 내부 - 로컬: 로컬
함수 내부 - 전역: 전역
함수 외부 - 전역: 전역
수정 후: 수정된 전역

모듈과 패키지

예제 8: 모듈 사용

# 표준 라이브러리 모듈 import
import math
from datetime import datetime, timedelta

print(f"pi: {math.pi}")
print(f"sqrt(16): {math.sqrt(16)}")
print(f"sin(pi/2): {math.sin(math.pi/2)}")

now = datetime.now()
print(f"\n현재 시간: {now}")

tomorrow = now + timedelta(days=1)
print(f"내일: {tomorrow}")

출력:

pi: 3.141592653589793
sqrt(16): 4.0
sin(pi/2): 1.0

현재 시간: 2024-01-15 14:30:45.123456
내일: 2024-01-16 14:30:45.123456

예제 9: 사용자 정의 모듈

calculator.py (모듈 파일):

"""간단한 계산기 모듈"""

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

def subtract(a, b):
    return a - b

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

def divide(a, b):
    if b == 0:
        raise ValueError("0으로 나눌 수 없습니다")
    return a / b

PI = 3.14159

메인 파일:

import calculator

print(f"3 + 5 = {calculator.add(3, 5)}")
print(f"10 - 4 = {calculator.subtract(10, 4)}")
print(f"6 * 7 = {calculator.multiply(6, 7)}")
print(f"20 / 4 = {calculator.divide(20, 4)}")
print(f"PI = {calculator.PI}")

출력:

3 + 5 = 8
10 - 4 = 6
6 * 7 = 42
20 / 4 = 5.0
PI = 3.14159

흔한 실수와 해결책

실수 1: 가변 기본값 사용

틀린 예제:

def add_to_list(item, lst=[]):
    lst.append(item)
    return lst

print(add_to_list(1))  # [1]
print(add_to_list(2))  # [1, 2] - 예상: [2]

올바른 예제:

def add_to_list(item, lst=None):
    if lst is None:
        lst = []
    lst.append(item)
    return lst

print(add_to_list(1))  # [1]
print(add_to_list(2))  # [2]

실수 2: 함수 내에서 외부 변수 수정

틀린 예제:

count = 0

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

# increment()

올바른 예제:

count = 0

def increment():
    global count
    count += 1
    return count

result = increment()
print(result)  # 1

실수 3: 반환값 확인하지 않음

틀린 예제:

def find_user(users, name):
    for user in users:
        if user["name"] == name:
            return user

result = find_user([], "Alice")
print(result.get("email"))  # AttributeError!

올바른 예제:

def find_user(users, name):
    for user in users:
        if user["name"] == name:
            return user
    return None

result = find_user([], "Alice")
if result:
    print(result.get("email"))
else:
    print("사용자를 찾을 수 없습니다")

연습 문제

문제 1: 팩토리얼 함수

정수 n을 받아 n!을 계산하는 함수를 작성하세요.

문제 2: 리스트 필터링 함수

숫자 리스트에서 특정 조건을 만족하는 요소들만 반환하는 함수를 작성하세요.

문제 3: 딕셔너리 병합 함수

두 개의 딕셔너리를 받아 합친 새로운 딕셔너리를 반환하는 함수를 작성하세요.

풀이

문제 1 풀이

def factorial(n):
    """팩토리얼 계산"""
    if n < 0:
        raise ValueError("음수는 불가능합니다")
    if n == 0 or n == 1:
        return 1
    return n * factorial(n - 1)

print(f"5! = {factorial(5)}")  # 120
print(f"10! = {factorial(10)}")  # 3628800

문제 2 풀이

def filter_list(numbers, condition):
    """조건을 만족하는 요소 필터링"""
    return [x for x in numbers if condition(x)]

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = filter_list(numbers, lambda x: x % 2 == 0)
odds = filter_list(numbers, lambda x: x % 2 != 0)

print(f"짝수: {evens}")
print(f"홀수: {odds}")

문제 3 풀이

def merge_dicts(dict1, dict2):
    """두 딕셔너리 병합"""
    result = dict1.copy()
    result.update(dict2)
    return result

d1 = {"a": 1, "b": 2}
d2 = {"c": 3, "d": 4}
merged = merge_dicts(d1, d2)
print(f"병합 결과: {merged}")  # {'a': 1, 'b': 2, 'c': 3, 'd': 4}

마무리

좋은 함수 설계는 코드의 재사용성과 유지보수성을 극대화합니다. 함수는 한 가지 일만 잘하도록 설계하고, 명확한 이름과 문서화로 의도를 드러내세요.

← 이전자료구조 완벽 가이드 다음 →객체지향 프로그래밍