Python에서 사전 이동

10041 단어 trickspython
빠른 팁 시간!

오늘 #100DaysOfCode 챌린지를 다시 시작했습니다(백만 번째). 저는 이 도전에 실제로 성공하기로 결심했고 포기하지 않습니다. 이번에는 Python Bytes Code Challenges website과 그들의 100일 프로젝트 제안을 사용하고 있습니다. 오늘의 도전을 통해 공유하고 싶은 사전 작업에 대한 깔끔한 작은 요령을 배웠습니다.

도전



문제는 dictionary of words 을 통과하는 것입니다. 실제로는 /usr/share/dict/words 의 복사본입니다. 다음 문자 점수를 사용하여 Scrabble에서 가장 높은 점수를 받은 단어를 찾습니다.

SCRABBLE_SCORES = [
  (1, "E A O I N R T L S U"),
  (2, "D G"),
  (3, "B C M P"),
  (4, "F H V W Y"), 
  (5, "K"), 
  (8, "J X"), 
  (10, "Q Z"),
]
LETTER_SCORES = {
    letter: score for score, letters in scrabble_scores
    for letter in letters.split()
}
# {"A": 1, "B": 3, "C": 3, "D": 2, ...}


문제



문제는 입력에 유효하지 않은 문자가 있는지 여부에 대해 걱정하고 싶지 않다는 것입니다(적어도 지금은). 따라서 "snoot!43@@@"라는 단어를 검색하면 지금은 SNOOT에 대한 점수를 보고 나머지 문자에 대해 0점을 보고 싶습니다. 이를 수행하는 방법은 여러 가지가 있다는 것을 알고 있지만 내 머릿속에 떠오른 첫 번째 방법은 기본값 0을 사용하는 것이었습니다(즉, LETTER_SCORES에 없는 문자를 찾으려고 하면 대신 0을 반환합니다. 제기 KeyError .)

DefaultDict 입력



다행스럽게도 Python은 표준 라이브러리의 defaultdict 모듈 덕분에 우리가 필요로 하는 것과 정확히 일치하는 collections 를 제공합니다. 사용법은 상당히 간단합니다. 입력을 찾을 수 없는 경우 기본값을 구성하는 클래스 또는 함수를 defaultdict에 제공합니다. 보여드리겠습니다.

from collections import defaultdict

zeros = defaultdict(int)
zeros["a"] = 1
zeros["b"] = zeros["definitely not in there"] + 4
print(zeros)
# => defaultdict(<int>, {"a": 1, "b": 4, "definitely not in there": 0})

zeros dict는 "definitely not in there" 키를 찾을 수 없기 때문에 default-maker 함수인 int 를 호출합니다. 계속해서 Python REPL을 열고 인수 없이 int 함수를 호출해 보십시오.

>>> int()
0


인수 없이 호출된 int 함수는 매번 0을 반환합니다.

자신만의 default-maker 함수를 만들 수도 있습니다(그리고 클래스도 작동합니다)!

from random import choice

def confusing_default():
    possibles = ["1", 1, True, "banana"]
    return choice(possibles)

tricky_dict = defaultdict(confusing_default)
tricky_dict["Ryan"]
# => "banana"
tricky_dict["Python"]
# => True
tricky_dict["Why would you do this?"]
# => 1
tricky_dict
# => defaultdict(<confusing_default>, {"Ryan": "banana", "Python": True, "Why would you do this?": 1})


종종 lambdas 를 사용하면 작업을 조금 더 빠르게 수행할 수 있습니다.

from random import randint

SCREAMING = defaultdict(lambda: "A")
for i in range(20):
    key = randint(0, 3)
    SCREAMING[key] += "A"
SCREAMING
# => defaultdict(<function <lambda> at 0x108707f28>, {0: 'AAAAAAAA', 1: 'AAAAAAA', 3: 'AAAAA', 2: 'AAAA'})


사실 defaultdict(lambda: 0) 를 사용하는 것이 defaultdict(int) 를 사용하는 것보다 더 명확하고 혼란이 덜하다고 생각합니다.

DefaultDict로 업그레이드



이제 마지막으로 빠른 팁을 받을 준비가 되었습니다. 위에서 나는 defaultdicts를 평범하고 오래된 파이썬LETTER_SCORES으로 정의했습니다. 원하는 기본 동작을 빠르게 얻으려면 어떻게 해야 합니까? 한 가지 방법은 두 개의 사전을 병합하는 내장dict 기능을 사용하는 것입니다.

FORGIVING_SCORES = defaultdict(lambda: 0)
FORGIVING_SCORES.update(LETTER_SCORES)

FORGIVING_SCORES["Q"]
# => 10

FORGIVING_SCORES["@"]
# => 0


만세!

물론 이것은 dict.update() defaultdict가 유효하지 않은 요청을 각각 저장하기 때문에 완벽한 솔루션은 아닙니다. 엄청난 수의 유효하지 않은 조회가 예상되지 않는다면 괜찮을 것입니다. 하지만 공간 효율성을 유지하는 것이 걱정된다면 다음과 같이 하는 것이 더 나을 것입니다.

score = LETTER_SCORES.get("@") or 0

FORGIVING_SCORES 함수는 get가 발생하면 None를 반환하고 KeyError를 사용하면 조회가 잘못되면 정상적인 기본값을 제공할 수 있습니다. 그리고 모두가 행복합니다!

2018년 4월 9일 수정: 지적한 바와 같이 기본 값or을 제공하면 훨씬 더 간단하게 이 작업을 수행할 수 있습니다.

score = LETTER_SCORES.get("@", 0)


마무리



결과적으로 이 블로그 게시물의 전체 이유는 초기 문제에 대한 가장 간단한 해결책이 아니었습니다. 즉, get의 작동 방식과 defaultdict 방법에 대해 조금 더 배우게 되었기를 바랍니다.

읽어 주셔서 감사합니다!


dict.update 에 원래 게시됨

좋은 웹페이지 즐겨찾기