함수의 추상화 철학, 일급 객체로서의 특성, 그리고 Google Style docstring 작성법을 배운다.
🎯 이 강의에서 배우는 것
왜 함수가 존재하는가? 코드를 줄이는 것만이 아닙니다. 함수는 복잡성을 다루는 인류의 오랜 지혜인 추상화(abstraction)의 구현입니다. 이 강의에서는 함수의 철학적 의미, 파이썬에서 함수가 일급 객체(first-class object)인 이유와 그 실전 활용, 그리고 좋은 docstring을 작성하는 방법까지 깊이 이해합니다.
🧠 함수가 존재하는 이유 — 추상화의 철학
인간의 뇌는 동시에 처리할 수 있는 정보량에 한계가 있습니다(심리학의 "매직 넘버 7±2"). 함수는 세부 구현을 숨기고 이름으로 접근하게 하여, 인간이 프로그램의 고수준 구조를 이해할 수 있게 합니다:
# 함수 없이
import hashlib, secrets
salt = secrets.token_bytes(32)
dk = hashlib.pbkdf2_hmac("sha256", "password".encode(), salt, 100_000)
stored = salt + dk
# 이게 무엇을 하는가? 추상화 없이는 알기 어려움
# 함수로 추상화
def hash_password(password: str) -> bytes:
salt = secrets.token_bytes(32)
dk = hashlib.pbkdf2_hmac("sha256", password.encode(), salt, 100_000)
return salt + dk
stored = hash_password("my_password")
# 이제 "비밀번호를 해시한다"는 의미가 명확
# DRY 원칙: Don't Repeat Yourself
# 중복 코드의 문제: 수정할 때 모든 곳을 바꿔야 함
# 함수: 한 곳만 수정하면 모든 호출부가 자동으로 바뀜
def calculate_vat(price: float, rate: float = 0.1) -> float:
return price * (1 + rate)
product_a = calculate_vat(10000) # 11000
product_b = calculate_vat(25000) # 27500
# 세율 변경 시 함수 하나만 수정하면 됨
🔧 def 문의 실행 — 함수는 런타임에 생성된다
import dis
# def 문은 선언이 아닌 실행
# 파이썬이 def 문을 만나면:
# 1. 함수 코드 객체(code object)를 컴파일
# 2. 함수 객체(function object)를 힙 메모리에 생성
# 3. 함수 이름을 현재 스코프의 변수에 바인딩
def greet(name: str) -> str:
return f"안녕하세요, {name}님!"
# 이미 함수 객체가 존재함
print(type(greet)) # <class 'function'>
print(id(greet)) # 메모리 주소
print(greet.__name__) # 'greet'
print(greet.__code__) # 코드 객체
# def가 런타임에 실행되므로, 조건부 함수 정의 가능
import sys
if sys.platform == "win32":
def get_home():
import os
return os.environ.get("USERPROFILE", "C:/Users")
else:
def get_home():
import os
return os.environ.get("HOME", "/home")
print(get_home()) # 플랫폼에 따라 다른 구현이 실행됨
# 함수를 다시 정의하면 이름이 새 객체를 가리킴
def add(a, b):
return a + b
old_add = add # 기존 함수 저장
def add(a, b): # add를 재정의
print(f"add({a}, {b}) 호출")
return a + b
add(1, 2) # 새 함수 (출력 포함)
old_add(1, 2) # 기존 함수 (출력 없음)
🏆 함수는 일급 객체 — 4가지 특성
import math
# 특성 1: 변수에 할당 가능
my_sqrt = math.sqrt
print(my_sqrt(16)) # 4.0 (math.sqrt와 동일한 객체)
# 특성 2: 함수의 인자로 전달 가능
def apply(func, value):
return func(value)
print(apply(math.sqrt, 25)) # 5.0
print(apply(abs, -42)) # 42
print(apply(str.upper, "hello")) # HELLO
# sorted의 key 파라미터: 함수를 인자로
words = ["banana", "apple", "cherry", "date"]
print(sorted(words)) # 사전순
print(sorted(words, key=len)) # 길이순
print(sorted(words, key=lambda w: w[-1])) # 마지막 글자순
# 특성 3: 함수의 반환값으로 사용 가능
def make_multiplier(factor):
def multiply(x):
return x * factor
return multiply # 함수를 반환!
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
# 특성 4: 자료구조에 저장 가능
operations = {
"add": lambda a, b: a + b,
"sub": lambda a, b: a - b,
"mul": lambda a, b: a * b,
"div": lambda a, b: a / b,
}
op = "add"
result = operations[op](10, 3)
print(result) # 13
# 함수 목록으로 일괄 처리
transforms = [str.strip, str.lower, str.title]
text = " HELLO WORLD "
for t in transforms:
text = t(text)
print(text) # "Hello World"
🔍 함수 객체의 속성
def calculate(x: int, y: int = 10) -> int:
"""두 수를 더한다.
Args:
x: 첫 번째 정수
y: 두 번째 정수 (기본값 10)
Returns:
두 수의 합
"""
result = x + y
return result
# 함수 메타데이터
print(calculate.__name__) # 'calculate'
print(calculate.__doc__) # docstring
print(calculate.__annotations__) # {'x': int, 'y': int, 'return': int}
print(calculate.__defaults__) # (10,) ← y의 기본값
# 코드 객체 (함수 내부 구조)
code = calculate.__code__
print(code.co_varnames) # ('x', 'y', 'result') ← 지역 변수 목록
print(code.co_argcount) # 2 ← 인자 수
print(code.co_filename) # 소스 파일 경로
print(code.co_firstlineno) # 함수 정의 시작 줄 번호
📖 docstring 작성법 — Google Style vs NumPy Style
# Google Style (가장 널리 사용, PEP 257 기반)
def divide(dividend: float, divisor: float) -> float:
"""두 수를 나눈다.
Args:
dividend: 나눠지는 수 (분자)
divisor: 나누는 수 (분모). 0이면 안 됨.
Returns:
나눗셈 결과 (float)
Raises:
ZeroDivisionError: divisor가 0일 때
TypeError: 숫자가 아닌 값이 전달될 때
Examples:
>>> divide(10, 2)
5.0
>>> divide(7, 3)
2.3333333333333335
"""
if divisor == 0:
raise ZeroDivisionError("divisor는 0이 될 수 없습니다")
return dividend / divisor
# NumPy Style (과학 계산 라이브러리에서 많이 사용)
def moving_average(data: list, window: int) -> list:
"""이동 평균을 계산한다.
Parameters
----------
data : list of float
시계열 데이터
window : int
이동 평균 윈도우 크기
Returns
-------
list of float
이동 평균 값 목록 (길이: len(data) - window + 1)
Notes
-----
window가 data 길이보다 크면 빈 리스트를 반환한다.
"""
return [sum(data[i:i+window]) / window for i in range(len(data) - window + 1)]
# help()로 docstring 확인
help(divide)
# doctest 모듈: docstring의 >>> 예제를 실제로 실행하여 검증
import doctest
# doctest.testmod() ← 모듈 내 모든 docstring 테스트
⚠️ 자주 하는 실수
- 함수를 호출하지 않고 참조만 전달:
sorted(words, key=len)에서key=len()이라고 쓰면 TypeError (len()은 str을 요구하는데 None을 받음). key에는 함수 객체를 전달하고 sorted가 내부에서 호출합니다. - docstring을 일반 주석처럼 작성:
"""설명"""은 첫 번째 표현식으로 문자열 리터럴을 두는 것입니다.#주석은 __doc__에 저장되지 않습니다. 인터프리터가 접근하려면 docstring을 사용하세요. - 함수를 너무 길게 작성: 함수가 화면 스크롤이 필요할 정도로 길면 두 가지 이상의 일을 하고 있을 가능성이 높습니다. 단일 책임 원칙(Single Responsibility Principle)에 따라 분리하세요.
📝 정리 및 다음 강의 예고
- 함수는 추상화의 도구 — 복잡한 구현을 이름으로 감추어 인간이 이해할 수 있게 합니다.
- def 문은 실행 시점에 함수 객체를 생성합니다 — 선언이 아니라 실행입니다.
- 파이썬 함수는 일급 객체 — 변수에 담고, 인자로 넘기고, 반환값으로 쓰고, 자료구조에 저장할 수 있습니다.
- Google Style docstring으로 함수의 인자, 반환값, 예외를 문서화하세요.
다음 강의: 매개변수 완전 분석 — 위치 인수, 키워드 인수, 가변 인수(*args, **kwargs), 기본값 함정까지 모든 매개변수 패턴을 마스터합니다.
관련 주제
- 함수 추상화 개념
- 일급 객체(First-class)
- 함수를 인자로 전달
- docstring 작성법(Google style)
- func.__code__ 속성
- DRY 원칙
- 개발·프로그래밍
- 개발·프로그래밍 강의
- 파이썬 기초 40강 — 처음 배우는 프로그래밍
- 무료강의
- 무료 온라인 강의
- NUGUNA
- 누구나
📚 시리즈 전체 공유
파이썬 기초 40강 — 처음 배우는 프로그래밍
이 강의가 속한 시리즈는 총 32강, 모두 무료입니다. 처음부터 배우려는 동료에게 시리즈 전체를 알려 주세요.
댓글
0/1000
불러오는 중...
