모든 강의를 무료로 볼 수 있어요. 회원가입 없이도 학습 가능합니다.

17강 / 전체 32강

함수란 무엇인가 — 추상화, 일급 객체, docstring

10분 읽기 조회 5

함수의 추상화 철학, 일급 객체로서의 특성, 그리고 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

불러오는 중...