names = ['소크라테스', '아르키메데스', '플라톤', '아리스토텔레스']
names.sort(key=len)
print(names)
['플라톤', '소크라테스', '아르키메데스', '아리스토텔레스']
def log_missing():
print('키 추가됨')
return 0
원본 딕셔너리와 변경할 내용이 주어진 경우, log_missing 함수는 로그를 두번 남길 수 있다.
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
print(result)
print(count)
defaultdict(<function increment_with_report..missing at 0x000002106229B670>, {'초록': 12, '파랑': 20, '빨강': 5, '주황': 9})
2
하지만 상태를 다루기 위한 훅으로 클로저를 사용하면 상태가 없는 함수에 비해 읽고 이해하기 어렵다. 다른 접근방법은 여러분이 추적하고 싶은 상태를 저장하는 작은 클래스를 정의하는 것이다.
class CountMissing:
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
class BetterCountMissing:
def __init__(self):
self.added = 0
def __call__(self):
self.added += 1
return 0
counter = BetterCountMissing()
assert counter() == 0
assert callable(counter)
다음 코드는 BetterCountMssing 인스턴스를 defaultdict의 디폴트 값 훅으로 사용해서 존재하지 않는 키에 접근한 횟수를 추적한다.
counter = BetterCountMissing()
result = defaultdict(counter, current) # __call__에 의존함
for key, amount in increments:
result[key] += amount
assert counter.added == 2
이 코드가 CountMissing.missing을 사용한 코드보다 깔끔하다.
call메서드는 함수가 인자로 쓰일 수 있는 부분에 이 클래스의 인스턴스를 사용할 수 있다는 사실을 나타낸다.
코드를 처음 읽는 사람도 이 클래스의 동작을 알아보기 위한 시작점이 call이라는 사실을 쉽게 알 수 있다.
이 클래스를 만든 목적이 상태를 저장하는 클로저 역할이라는 사실을 잘 알 수 있다.
무엇보다 가장 좋은 점은 defaultdict가 call 내부에서 어떤 일이 벌어지는지에 대해 알 필요가 없다.
defaultdict에게 필요한 것은 키가 없는 경우를 처리하기 위한 디폴트 값 훅 뿐이다.
파이썬은 단순한 함수 인터페이스를 만족시킬 수 있는 여러가지 방법을 제공한다.