딕셔너리의 내용은 동적이므로 어떤 키에 접근하거나 키를 삭제할 때 그 키가 딕셔너리에 없을 수도 있다.
counters = {
'품퍼니켈': 2,
'사워도우': 1,
}
투표를 할때 없는 키에 대해서는 아래와 같이 처리해야 한다.
key = '밀'
if key in counters:
count = counters[key]
else:
count = 0
counters[key] = count + 1
예외를 활용한 방법도 있다
try:
count = counters[key]
except KeyError:
count = 0
counters[key] = count + 1
이런 겨우에는 dict 내장 메서드인 get 을 사용하자
get의 두번째 인자는 첫번째 인자인 키가 딕셔너리에 들어 있지 않을 떄 돌려주는 디폴트 값이다
count = counters.get(key, 0)
counters[key] = count + 1
다양한 방법으로 표현할 수 있으나, 대부분 대입을 중복 사용해야 하므로 코드 가독성이 떨어진다.
if key not in counters:
counters[key] = 0
counters[key] += 1
if key in counters:
counters[key] += 1
else:
counters[key] = 1
try:
counters[key] += 1
except KeyError:
counters[key] = 1
get을 쓰자
그리고 위와 같은 경우 collection 모듈에 존재하는 Counter를 고려해보자
딕셔너리에 저장된 값이 리스트처럼 더 복잡한 값일 경우
또한 특표수만 세는 것이 아니라 어떤 사람이 어떤 유형의 빵에 투표했는지도 알고 싶을 때
이런 경우에는 각 키마다 이름이 들어 있는 리스트를 연관 시킬 수 있다.
votes = {
'바게트': ['철수', '순이'],
'치아바타': ['하니', '유리'],
}
key = '브리오슈'
who = '단이'
if key in votes:
names = votes[key]
else:
votes[key] = names = []
names.append(who)
print(votes)
{'바게트': ['철수', '순이'], '치아바타': ['하니', '유리'], '브리오슈': ['단이']}
이 예제는 키가 존재하지 않을 때 맹목적으로 빈 리스트를 디폴트 값으로 대입할 수 있다.
이중 대입문인 votes[key] = names = [] 는 키 대입을 두 줄이 아니라 한 줄로 처리한다.
names = []
votest[key] = names
딕셔너리 값이 리스트인 경우 KeyError 예외가 발생하므로 예외처리를 이용하면 더 간편하다
try:
names = votes[key]
except KeyError:
votes[key] = names = []
names.append(who)
마찬가지로 get을 사용 할 수 있다.
names = votes.get(key)
if names is None:
votes[key] = names = []
names.append(who)
names = votes.get(key, [])
names.append(who)
if (names := votes.get(key)) is None:
votes[key] = names = []
# 파이썬 버전문제
names.append(who)
File "<ipython-input-19-256beb912819>", line 1 if (names := votes.get(key)) is None: ^ SyntaxError: invalid syntax
dict 타입은 이 패턴을 더 간단히 사용할 수 있게 해주는 setdefault 메서드를 제공한다.
names = votes.setdefault(key, [])
names.append(who)
키가 없으면 setdefault에 전달된 디폴트 값이 별도로 복사되지 않고 딕셔너리에 직접 대입된다.
data = {}
key = 'foo'
value = []
data.setdefault(key, value)
print('이전:', data)
value.append('hello')
print('이후: ', data)
이전: {'foo': []} 이후: {'foo': ['hello']}
따라서 매 호출마다 새로 만들어줘야 한다.
처음 예제에서는 왜 setdefault를 사용하지 않았을까?
count = counters.setdefault(key, 0)
counters[key] = count + 1
setdefault의 경우 위에서 처럼 대입을 두번 한다.
하지만 get은 대입을 한번만 한다.
따라서 get이 더 효율적이다
setdefault를 쓰기보다는 defaultdict이 더 좋을 수 있다.
get은 default 값을 리턴해주고, setdefault는 key에 대한 value를 직접 대입해준다
t = {
"a": 1,
"b": 2
}
t
{'a': 1, 'b': 2}
t.get("a")
1
t.get("c")
t.get("c", 3)
3
t.setdefault("c", 3)
3
t
{'a': 1, 'b': 2, 'c': 3}