728x90
python 3.5
- python 3.5 이전에는 딕셔너리에 대해 이터레이션을 수행하면 키를 임의의 순서로 돌려줬으며, 이터레이션 순서는 원소가 삽입된 순서와 일치하지 않았다.
- 이런 현상은 예전의 딕셔너리 구현이 내장 hash 함수와 seed를 사용하는 해시테이블 알고리즘으로 만들어졌기 때문이다.
python 3.6 ~
- python 3.6부터는 삽입 순서를 보존하도록 동작이 개선됐다.
**kwargs 순서
- 함수에 대한 키워드 인자는 예전에는 순서가 뒤죽박죽이었고, 그로 인해 함수 호출을 디버깅 하기 힘들었다.
def my_func(**kwargs):
for key, value in kwargs.items():
print(f'{key} = {value}')
my_func(goose='gosling', kangaroo='joey') # 3.5와 3.7이후가 다름
Class 순서
- 클래스도 인스턴스 딕셔너리에
dict타입을 사용한다.
class MyClass:
def __init__(self):
self.alligator = 'hatchling'
self.elephant = 'calf'
a = MyClass()
for key, value in a.__dict__.items():
print(f'{key} = {value}')
항상 삽입 순서에 의존하지 말자
- 파이썬에서는 프로그래머가
list,dict등의 표준 프로토콜을 흉내 내는 커스텀 컨테이너 타입을 쉽게 정의할 수 있다. - 파이썬은 정적 타입 지정 언어가 아니기 때문에 대부분 객체의 동작이 객체의 실질적인 타입을 결정하는 덕 타이핑 에 의존하며, 이로 인해 어려움에 빠질 수 있다.
- 아래는 value를 기준으로 순위를 매긴
dict이다.- otter, fox, polar bear 가 1,2,3 순서대로 나올 것이다.
votes = {
'otter': 1281,
'polar bear': 587,
'fox': 863,
}
def populate_ranks(votes, ranks):
names = list(votes.keys())
names.sort(key=votes.get, reverse=True)
for i, name in enumerate(names, 1):
ranks[name] = i
def get_winner(ranks):
return next(iter(ranks))
ranks = {}
populate_ranks(votes, ranks)
print(ranks)
winner = get_winner(ranks)
print(winner)
- 요구사항이 변경되어 등수가 아니라 알파벳순으로 표기해야한다고 가정하자.
- 이때
collections.abc로 알파벳 순서대로 이터레이션을 정의해주는 클래스를 정의할 수 있다.
from collections.abc import MutableMapping
class SortedDict(MutableMapping):
def __init__(self):
self.data = {}
def __getitem__(self, key):
return self.data[key]
def __setitem__(self, key, value):
self.data[key] = value
def __delitem__(self, key):
del self.data[key]
def __iter__(self):
keys = list(self.data.keys())
keys.sort()
for key in keys:
yield key
def __len__(self):
return len(self.data)
SortedDict은 표준 딕셔너리 프로토콜을 지키므로, 앞에서 에러가 나진 않지만 원하는 데로 동작하지 않는다.
sorted_ranks = SortedDict()
populate_ranks(votes, sorted_ranks)
print(sorted_ranks.data)
winner = get_winner(sorted_ranks)
print(winner)
- 이 코드는
dict대신SortedDict를 사용하므로 삽입 순서가 출력에 일치한다는 가정은 성립하지 않는다.- 크게 3가지 방식이 있다.
- 첫 번째는 하드코딩을 통해 winner를 얻을 수 있다.
def get_winner(ranks):
for name, rank in ranks.items():
if rank == 1:
return name
winner = get_winner(sorted_ranks)
print(winner)
- 두 번째는 타입을 체크해
dict일 때만 결과를 나타내는 것이다.
def get_winner(ranks):
if not isinstance(ranks, dict):
raise TypeError('dict 인스턴스가 필요합니다')
return next(iter(ranks))
- 세 번째는 타입 애너테이션을 사용해서
get_winner에 전달되는 값이 딕셔너리와 비슷한 동작을 하는MutableMapping인스턴스가 아니라dict인스턴스가 되도록 강제하는 것이다. - 다음은 앞의 코드에 타입 애너테이션을 붙이고 mypy 도구를 엄격한 모드로 사용한다.
from typing import Dict, MutableMapping
def populate_ranks(votes: Dict[str, int],
ranks: Dict[str, int]) -> None:
names = list(votes.keys())
names.sort(key=votes.get, reverse=True)
for i, name in enumerate(names, 1):
ranks[name] = i
def get_winner(ranks: Dict[str, int]) -> str:
return next(iter(ranks))
class SortedDict(MutableMapping[str, int]):
def __init__(self):
self.data = {}
def __getitem__(self, key):
return self.data[key]
def __setitem__(self, key, value):
self.data[key] = value
def __delitem__(self, key):
del self.data[key]
def __iter__(self):
keys = list(self.data.keys())
keys.sort()
for key in keys:
yield key
def __len__(self):
return len(self.data)728x90
'Programming Language > Python' 카테고리의 다른 글
| BETTER WAY 26 - functools.wrap 사용해 함수 데코레이터를 정의하라 (0) | 2024.11.26 |
|---|---|
| BETTER WAY 21 - 변수 영역과 클로저의 상호작용 방식을 이해하라 (0) | 2024.11.25 |
| BETTER WAY 14 - 복잡한 기준을 사용해 정렬할 때는 key 파라미터를 사용하라 (0) | 2024.10.14 |
| BETTER WAY 13 - 슬라이싱보다는 나머지를 모두 잡아내는 언패킹을 사용하라 (1) | 2024.10.10 |
| BETTER WAY 12 - 스트라이드와 슬라이스를 한 식에 함께 사용하지 말라 (0) | 2024.09.20 |