들어가며
함수는 코드 재사용성과 가독성을 높이는 핵심 요소입니다. 좋은 함수 설계는 프로젝트의 유지보수성을 크게 향상시킵니다.
함수의 기본
예제 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}
마무리
좋은 함수 설계는 코드의 재사용성과 유지보수성을 극대화합니다. 함수는 한 가지 일만 잘하도록 설계하고, 명확한 이름과 문서화로 의도를 드러내세요.