딕셔너리 키가 없는 경우 get을 사용하라
10 Nov 2021
[Effective Python] Better way 16: in을 사용하고 딕셔너리 키가 없을 때 KeyError를 처리하기 보다는 get을 사용하라
- 딕셔너리 키가 없는 경우를 처리하는 방법으로는 In식을 사용하는 방법, KeyError 예외를 처리하는 방법, get 메서드를 사용하는 방법, setdefault 메서드를 사용하는 방법이 있다.
- 기본적인 타입의 값이 들어가는 딕셔너리나 리스트와 같은 비용이 비싼 자료구조를 딕셔너리를 만들때 get 메서드를 사용하는 것이 가장 낫다.
이 chapter에서는 딕셔너리의 setdefault 메서드의 예외 및 비용과 관련된 내용을 다룹니다. 다음 코드를 보면 get과 setdefault의 동작이 다른것을 볼 수 있습니다.
>>> my_dict = {}
>>> my_dict.setdefault('some key', 'a value')
'a value'
>>> my_dict
{'some key': 'a value'}
>>> my_dict.get('some key2', 'a value2')
'a value2'
>>> my_dict
{'some key': 'a value'}
setdefault의 경우 key가 존재하지 않을 경우 default값을 갖고있는 새로운 키를 딕셔너리에 대입합니다. get의 경우 default값이 반환됩니다.
-
딕셔너리와 상호작용 하는 세가지 기본연산은 키나 키에 연관된 값에 접근,삭제,대입하는 것입니다.
counters ={ '소보로':1, '단팥빵':2, } key ='꽈베기' if key in counters: count = counters[key] else: count=0 counters[key]=count+1
위의 코드처럼 in 연산자를 사용하여 key의 존재 유무를 확인할 수 있습니다.
try: count = counters[key] except KeyError: count =0 counters[key]=count+1
다른 방법으로는 KeyErorr를 활용하는 방법도 있습니다. 하지만 위의 두 방법은 더 길고, 가독성이 좋지 못합니다.
count = counters.get(key,0) counters[key]= count+1
get 메서드를 사용하면 짧고, 간결하게 나타낼 수 있습니다.
-
딕셔너리에 저장된 값이 리스트와 같은 복잡한 값일 경우에 대해 살펴봅시다.
votes = { '소보로':['철수','민수'], '단팥빵':['미애','미정'], } key = '꽈베기' who = '종원' if key in votes: names = votes[key] else: votes[key]=names=[] names.append(who) print(votes) try: names = votes[key] except KeyError: votes[key]=names=[] names.append(who) print(votes)
같은 방식으로 in 또는 KeyError를 활용하여 위와 같이 정리할 수 있습니다.
if(names:=votes.get(key)) is None: votes[key] = names=[] names.append(who)
이 경우에도 get 메서드를 활용하는 방법이 더 가독성이 좋습니다. warlus연산을 사용하면 코드를 더 짧게 작성할 수 있습니다.
-
setdefault 메서드를 사용하여 같은 동작을 구현할 수 있습니다.
names = votes.setdefault(key,[]) names.append(who)
하지만 메서드 이름이 setdefault이고, 이는 메서드의 동작을 분명하게 나타내지 못합니다. (get_or_set이 아닌.) 또한 setdefault에 전달된 defualt값이 별도로 복사되지 않고 딕셔너리에 직접적으로 대입됩니다. 그래서 다음과 같은 오류를 발생시킬 수 있습니다.
data = {} key = 'foo' value = [] data.setdefault(key,value) print('이전',data) value.append('hello') print('이후',data) # 이전 {'foo': []} # 이후 {'foo': ['hello']}
키에 해당하는 디폴트값을 setdefault에 전달할 때마다 그 값을 새로 만들어야 한다는 뜻이고, 호출 할때마다 리스트를 만들어야 하므로 성능이 크게 저항 될 수 있습니다.
data = {} value = [] for i in range(3): key = i data.setdefault(key, value) value.append('hello') print(data) # {0: ['hello']} # {0: ['hello', 'hello'], 1: ['hello', 'hello']} # {0: ['hello', 'hello', 'hello'], 1: ['hello', 'hello', 'hello'], 2: ['hello', 'hello', 'hello']}
위와 같이 리스트를 매번 새로 만들어 성능이 저하 되는 동시에 unexpected 동작을 할 수 있습니다.
다음 챕터에선 같은 상황에서 setdeafualt보다 나은 선택지인 defaultdict에 대해 알아봅시다.