[Effective Python] | September 14, 2020
Effective Python: Second Edition 내용 정리
setdefault
가 missing key들을 처리할 때 더 짧은 코드로 사용할 수 있다. (Item 16 참조)
많은 경우에, collections의 defaultdict
가 더 나은 옵션이다.(Item 17 참조)
하지만, setdefault
도 defaultdict
도 맞지 않는 경우가 있다.
그럴 경우에는 __missing__
을 사용하라.
이 방식으로 사용할 수는 있지만, dictionary에 access 하는 횟수가 많고 구조가 더 복잡하다.
files = {}
name = 'a.txt'
if (handle := files.get(name)) is None:
try:
handle = open(name, 'a+b')
except OSError:
print(f"Can't open file {name}")
raise
else: #handle이 없는 경우
files[name] = handle
handle.seek(0)
file_data = handle.read()
Python File Handling (open options)
a
: append 모드 (파일이 존재하지 않으면 생성)
+
: updating 모드 (파일을 읽고 쓰기용으로 열기)
b
: binary 모드
OSError
This exception is raised when a system function returns a system-related error, including I/O failures such as “file not found” or “disk full” (not for illegal argument types or other incidental errors).
Ref. Python docs
file.seek(0)
파일의 0(맨처음) 찾기
이렇게 하고 read하면 파일을 처음부터 다시 읽기
open
function을 호출한다.Exceptions이 open 함수를 호출하면서 생긴 것인지 setdefault의 호출로 인해 생긴 것인지 구별할 수 없을 수도 있다.
files = {}
name = 'a.txt'
try:
handle = files.setdefault(name, open(name, 'a+b'))
except OSError:
print(f"Can't open file {name}")
raise
else:
handle.seek(0)
file_data = handle.read()
defaultdict로 pass 되는 function은 arguments가 필요 없어야 한다.
💡 dafaultdict(function(params))
형태로 정의하면, TypeError: first argument must be callable or None
발생
그래서 이대로 실행하면, open_file는 parameter를 받지못하고 error가 발생한다.
from collections import defaultdict
def open_file(name):
try:
return open(name, 'a+b')
except OSError:
print(f"Can't open file {name}")
raise
files = defaultdict(open_file)
name = 'a.txt'
handle = files[name]
handle.seek(0)
file_data = handle.read()
>>> TypeError: open_file() missing 1 required positional argument: 'name'
dict type의 subclass를 만들고, missing keys를 처리할 수 있는 __missing__
method를 구현한다.
files[name] 으로 dictionary에 access할 때, name key가 없다면, __missing__ method가 호출된다.
이미 key값이 있다면 missing method는 호출되지 않는다. (이것은 __getattr__
의 작동과 비슷하다. Item 47 참조)
def open_file(name):
try:
return open(name, 'a+b')
except OSError:
print(f"Can't open file {name}")
raise
class Files(dict):
def __missing__(self, key):
value = open_file(key)
self[key] = value
return value
files = Files()
name = 'a.txt'
handle = files[name]
handle.seek(0)
file_data = handle.read()