3줄 요약
__call__
메서드로 클래스의 인스턴스를 일반 파이썬 함수처럼 호출할 수 있게 해준다.# ex) sort() : key 훅으로 len 내장 함수를 전달
# key 후크로 넘겨서 이름 리스트를 길이로 정렬
names = ['소크라테스', '아르키메데스', '플라톤', '아리스토텔레스']
names.sort(key=len)
print(names)
>>
['플라톤', '소크라테스', '아르키메데스', '아리스토텔레스']
# 훅 : 키를 찾을 수 없을 때마다 로그를 남기고 기본값으로 0을 반환
def log_missing():
print('키 추가됨')
return 0
from collections import defaultdict
current = {'초록': 12, '파랑': 3}
increments = [
('빨강', 5),
('파랑', 17),
('주황', 9),
]
result = defaultdict(log_missing, current) # 인자로 참조
print('이전:', dict(result))
for key, amount in increments:
result[key] += amount
print('이후:', dict(result))
>>
이전: {'초록': 12, '파랑': 3}
키 추가됨
키 추가됨
이후: {'초록': 12, '파랑': 20, '빨강': 5, '주황': 9}
위의 함수에서, 상태 보존 클로저를 기본값 후크로 사용하는 헬퍼 함수 적용
def increment_with_report(current, increments):
added_count = 0
def missing():
nonlocal added_count # 상태 보존 클로저
added_count += 1
return 0
result = defaultdict(missing, current)
for key, amount in increments:
result[key] += amount
return result, added_count
result, count = increment_with_report(current, increments)
assert count == 2
이 함수의 문제는, 상태가 없는 함수의 예제보다 이해하기 어렵다는 점.
class CountMissing(object):
def __init__(self):
self.added = 0
def missing(self):
self.added += 1
return 0
counter = CountMissing()
result = defaultdict(counter.missing, current) # 메서드를 직접 참조
for key, amount in increments:
result[key] += amount
assert counter.added == 2
하지만 여기서도 문제, CountMissing이 어디에 쓰일지, missing
이라고 명명됐기에, 용도를 파악하기 어려움
__call__
로 명확하게 정의하자class BetterCountMissing(object):
def __init__(self):
self.added = 0
def __call__(self):
self.added += 1
return 0
counter = BetterCountMissing()
counter()
assert callable(counter)
counter = BetterCountMissing()
result = defaultdict(counter, current) # __call__ 사용
for key, amount in increments:
result[key] += amount
assert counter.added == 2
__call__
메서드는 (API 후크처럼) 함수 인수를 사용하기 적합한 위치에 클래스의 인스턴스를 사용할 수 있다는 사실을 드러낸다.