betterway15**kwargs

김승환·2021년 7월 11일

코딩의 기술

목록 보기
6/36

딕셔너리 삽입 순서에 의존할 때는 조심하라

  • 3.5버전에서는 딕셔너리를 저장할 때 강아지 -> 개 순으로 저장하더라도 개 --> 강아지순으로 저장되었다.
  • 위 문제는 개선되었다.
#개선된 모습
def my_func(**kwargs):
    for key, value in kwargs.items():
        print(f'{key} = {value}')

my_func(goose='gosling', kangaroo='joey') # 3.5와 3.7이후가 다름

goose = gosling
kangaroo = joey

class MyClass:
    def __init__(self):
        self.alligator = 'hatchling'
        self.elephant = 'calf'

a = MyClass()
for key, value in a.__dict__.items():
    print(f'{key} = {value}')

alligator = hatchling
elephant = calf

파이썬은 정적 타입 지정 언어가 아니기 때문에 대부분의 경우 코드는 엄격한 클래스 계층보다는 객체의 동작이 객체의 실질적인 타입을 결정하는 덕 타이핑에 의존한다.

덕 타이핑 : 객체가 실행 시점에 어떻게 행동하는지를 기준으로 객체의 타임을 판단하는 타입 지정 방식

# 예시 
# 동물 콘테스트에서 콘테스트 결과를 보여주는 프로그램을 작성
#ranks에 때하 등수 오름차순으로 등록한다고 가정하고 동작한다. 
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 # ranks라는 딕셔너리에 name이라는 key에 맞게 생성

def get_winner(ranks):
    return next(iter(ranks)) #첫번째가 무조건 1등이기 때문에 이터레이션을 한번이용하여 호출

ranks = {}
populate_ranks(votes, ranks)
print(ranks)
winner = get_winner(ranks)
print(winner)

{'otter': 1, 'fox': 2, 'polar bear': 3}
otter

# 두 번째 예시
# 프로그램의 예시가 변경되고 등수순이 아닌 알파벳 순 일때
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)

# 위 코드는 key 위치의 따라 오름차순으로 정렬 그리고 딕셔너리 저장.
    
sorted_ranks = SortedDict()
populate_ranks(votes, sorted_ranks) #populate_rank에는 이름 내림차순 투표내림차순으로 rank가 나오게 되어있음.
print(sorted_ranks.data) #결과값이 전과 동일하게 나옴
winner = get_winner(sorted_ranks) #SortedDict에 의해 제너레이터는 keys 알파벳 순으로 되어있음.
print(winner) # 랭크순이 아닌 알파벳 순으로 나옴.

{'otter': 1, 'fox': 2, 'polar bear': 3}
fox

get_winner 구현이 populate_ranks에 맞게 딕셔너리를 이터레이션한다고 가정을 했지만 dict대신 sorteddict을 사용하여 가정성립x

문제를 해결하는 세가지 방법

  • 첫번째는 ranks 딕셔너리가 어떤 특정 순서로 이터레이션 된다고 가정을 하지 않고 get_winner함수 구현
#단순히 rank가 1인 key를 호출
def get_winner(ranks):
    for name, rank in ranks.items():
        if rank == 1:
            return name

winner = get_winner(sorted_ranks)
print(winner)

otter

  • 두 번째 방법은 함수 맨 앞에 ranks의 타입이 우리가 원하는 타입인지 검사하는 함수를 추가
# isinstance 함수를 이용한 확인방법
def get_winner(ranks):
    if not isinstance(ranks, dict): # (인스턴스, 클래스/데이터 타입) 맞으면 true 호출
        raise TypeError('dict 인스턴스가 필요합니다')
    return next(iter(ranks))
get_winner(sorted_ranks)

TypeError Traceback (most recent call last)
in
4 raise TypeError('dict 인스턴스가 필요합니다')
5 return next(iter(ranks))
----> 6 get_winner(sorted_ranks)

in get_winner(ranks)
2 def get_winner(ranks):
3 if not isinstance(ranks, dict): # (인스턴스, 클래스/데이터 타입) 맞으면 true 호출
----> 4 raise TypeError('dict 인스턴스가 필요합니다')
5 return next(iter(ranks))
6 get_winner(sorted_ranks)

TypeError: dict 인스턴스가 필요합니다

  • 세 번째 방법은 타입 애너테이션을 사용하여 get_winner에 전달 되는 값이 딕셔너리와 비슷한 동작을 하는 mutableMapping인스턴스가 아닌 dict 인스턴스가 되도록 강제하는 것
# 각 딕셔너리의 타임을 지정함으로써 지정과 다른 결과가 있을 경우 에러에서 원인을 지적한다.
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)
    
votes = {
    'otter': 1281,
    'polar bear': 587,
    'fox': 863,
}


sorted_ranks = SortedDict()
populate_ranks(votes, sorted_ranks)
print(sorted_ranks.data)
winner = get_winner(sorted_ranks)
print(winner)

{'otter': 1, 'fox': 2, 'polar bear': 3}
fox

profile
인공지능 파이팅!

0개의 댓글