setdefault와 defaultdict 모두 사용하기가 적당하지 않은 경우가 있다.
pictures = {}
path = 'profile_1234.png'
if (handle := pictures.get(path)) is None:
try:
handle = open(path, 'a+b')
except OSError:
print(f'경로를 열 수 없습니다: {path}')
raise
else:
pictures[path] = handle
handle.seek(0)
image_data = handle.read()
setdefault를 활용하는 방법도 있다
try:
handle = pictures.setdefault(path, open(path, 'a+b'))
except OSError:
print(f'경로를 열 수 없습니다: {path}')
raise
else:
handle.seek(0)
image_data = handle.read()
이 코드는 문제가 많다.
파일 핸들을 만드는 내장 함수인 open이 딕셔너리에 경로가 있는지 여부와 관계없이 항상 호출된다.
내부 상태를 관리하려 한다면 프로필 사진의 상태를 관리하기 위해 defaultdict을 쓸 수 있다고 가정할수도 있다.
from collections import defaultdict
def open_picture(profile_path):
try:
return open(profile_path, 'a+b')
except OSError:
print(f'경로를 열 수 없습니다: {profile_path}')
raise
pictures = defaultdict(open_picture)
handle = pictures[path]
handle.seek(0)
image_data = handle.read()
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-7-951e7d5de294> in <module> 1 pictures = defaultdict(open_picture) ----> 2 handle = pictures[path] 3 handle.seek(0) 4 image_data = handle.read() TypeError: open_picture() missing 1 required positional argument: 'profile_path'
문제는 defaultdict 생성자에 전달한 함수는 인자를 받을수 없다는데 있다.
이는 defaultdict이 호출하는 도우미 함수가 처리 중인 키를 알 수 없다는 뜻이다.
이로 인해 파일 경로를 사용해 open을 호출할 방법이 없다.
이런 상황에서는 setdefault와 defaultdict 모두 필요한 기능을 제공하지 못한다.
class Pictures(dict):
def __missing__(self, key):
value = open_picture(key)
self[key] = value
return value
pictures = Pictures()
handle = pictures[path]
handle.seek(0)
image_data = handle.read()
dict 타입의 하위 클래스를 만들고 __missing__ 특별 메서드를 구현하면 키가 없는 경우를 처리하는 로직을 커스텀화 할 수 있다.
pictures
{'profile_1234.png': <_io.BufferedRandom name='profile_1234.png'>}
defaultdict(open("aa.txt", 'a'))
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-12-b9ece99d44ca> in <module> ----> 1 defaultdict(open("aa.txt", 'a')) TypeError: first argument must be callable or None