C.W.K.
Stream
Lesson 02 of 06 · published

functools — cache / partial / reduce / wraps

~20 min · functools, cache, partial, reduce, lru_cache

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

함수 모양 유틸리티

itertools 가 iterable 용이면 functools 는 함수용. 결과 캐싱, 인자 currying, 시퀀스 reducing, 데코레이터 wrapper 간 메타데이터 보존. 첫 알아둘 4 — cache/lru_cache, partial, reduce, wraps (decorators 트랙에서).

cache + lru_cache — 무료 메모이제이션

@functools.cache (3.9+) 가 인자 기반으로 결과 캐시 — 같은 인자, 캐시된 값 반환. @lru_cache(maxsize=128) 가 같은데 bounded LRU 캐시. 순수 함수가 반복 호출에 극적으로 빨라져. 정석 데모 — 재귀 피보나치가 지수에서 선형으로.

partial — 인자 미리 바인딩

functools.partial(fn, *args, **kwargs) 가 일부 인자 미리 채워진 새 callable 반환. 더 적은 인자 기대하는 인터페이스에 함수 적응 — 콜백, sort key, 설정이 박힌 이벤트 핸들러로 넘기기.

reduce — iterable 위에 left fold

functools.reduce(fn, iterable) 가 왼쪽부터 누적적으로 fn 적용, iterable 을 단일 값으로 줄임. reduce(operator.add, [1, 2, 3])((1+2)+3). reduce 의 대부분 사용은 이제 sum, min, max, 또는 컴프리헨션으로 더 잘 표현. 커스텀 결합 함수 필요할 때 손에 닿아.

War Story: cachelru_cache 는 인자 해싱으로 작동. 함수가 unhashable 타입 (list, dict) 받으면 캐시 데코레이터가 raise. 해결 — hashable 형태로 변환하는 wrapper, 또는 더 옵션 있는 cachetools.

Code

cache + lru_cache — 메모이제이션·python
from functools import cache, lru_cache
import time

# 캐시 없이 — 재귀 피보나치 지수적
def fib_slow(n):
    if n < 2:
        return n
    return fib_slow(n - 1) + fib_slow(n - 2)

# 캐시로 — 선형
@cache
def fib_fast(n):
    if n < 2:
        return n
    return fib_fast(n - 1) + fib_fast(n - 2)

# fib_slow(35) ~3 초
# fib_fast(35) 즉시
print(fib_fast(50))            # 12586269025  — 거대한 n 에도 작동

# size 제한 lru_cache
@lru_cache(maxsize=128)
def expensive(x):
    print("computing", x)
    return x * x

print(expensive(3))            # computing 3, 9 반환
print(expensive(3))            # 캐시, 출력 X
print(expensive.cache_info())  # CacheInfo(hits=1, misses=1, maxsize=128, currsize=1)
partial — 인자 미리 바인딩·python
from functools import partial

def multiply(x, y):
    return x * y

double = partial(multiply, 2)         # x=2 미리 바인딩
print(double(5))                      # 10
print(double(10))                     # 20

# 콜백에 유용
def on_event(event_type, payload):
    print(f"got {event_type}: {payload}")

login_handler = partial(on_event, "login")
login_handler({"user": "alice"})      # got login: {'user': 'alice'}

# kwargs 미리 바인딩
import json
pretty = partial(json.dumps, indent=2, sort_keys=True)
print(pretty({"b": 2, "a": 1}))
reduce — sum/min/max 부족할 때·python
from functools import reduce
import operator

# 대부분 reduce 가 빌트인 등가물 있음
print(sum([1, 2, 3, 4]))                                # 10
print(reduce(operator.add, [1, 2, 3, 4]))              # 10  (같음)

# reduce 가 값어치 — 커스텀 결합자
products = reduce(operator.mul, [1, 2, 3, 4], 1)       # 24
print(products)

# 커스텀 함수 — 가장 긴 문자열 찾기
words = ["hi", "hello", "hey", "howdy"]
longest = reduce(lambda a, b: a if len(a) >= len(b) else b, words)
print(longest)                                          # 'howdy'

# 근데 max() + key= 가 보통 더 명확
print(max(words, key=len))                              # 'howdy'
cache + unhashable 인자 — 함정·python
from functools import cache

@cache
def sum_list(items):
    return sum(items)

try:
    sum_list([1, 2, 3])      # list 가 unhashable
except TypeError as e:
    print("잡음:", e)

# 우회 — 경계에서 hashable 타입으로 변환
@cache
def sum_tuple(items_tuple):
    return sum(items_tuple)

print(sum_tuple((1, 2, 3)))   # 작동

# 또는 wrapper
def sum_list_v2(items):
    return sum_tuple(tuple(items))

External links

Exercise

(a) 이항 계수 (C(n, k) = C(n-1, k-1) + C(n-1, k)) 계산하는 재귀 함수에 @cache 적용 + 큰 입력 빠른지 확인. cache_info() 출력해서 hit/miss 봐. (b) partial 로 첫 인자가 [WARN] 으로 미리 바인딩된 printlog_warn 함수. (c) reduce + 커스텀 함수로 list 곱 계산 — math.prod 와 비교.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.