Skip to content
This page has been auto-translated and may contain errors.View in English

딕셔너리

docs.scrimba.com

리스트를 사용하면 위치로 항목을 찾을 수 있습니다. 하지만 이름으로 항목을 찾고 싶을 때가 많습니다. "3번 항목을 주세요"가 아니라 "민지의 점수를 주세요"라고 하고 싶을 때죠. 딕셔너리는 데이터를 키-값 쌍으로 저장합니다. 위치가 아니라 키로 값을 찾습니다.

리스트의 위치 인덱스가 의미가 없을 때는 딕셔너리가 적합한 구조입니다. 딕셔너리는 임의의 키를 값에 매핑하여 O(1) 시간에 이름으로 조회할 수 있게 해줍니다. 리더보드, JSON 응답, 설정 파일 모두 자연스럽게 키-값 매핑으로 표현됩니다.

dict는 평균 O(1) 조회, 삽입, 삭제를 제공하는 키-값 저장소입니다(O(1)은 딕셔너리 크기가 아무리 커도 비용이 일정하다는 뜻으로, 키를 해싱하여 숫자로 변환한 후 그 숫자가 바로 슬롯을 가리키기 때문입니다). 키는 해시 가능해야 하는데, 이는 내용이 절대 바뀌지 않아 그 숫자가 안정적으로 유지된다는 뜻입니다. str, int, tuple은 가능하지만 list는 불가능합니다. 값은 어떤 객체든 될 수 있습니다. Python 3.7 이후로 딕셔너리는 삽입한 순서를 유지하므로, 직렬화나 표시할 때 이것이 중요합니다. 같은 구조가 Python 내부 곳곳에서 사용되는데, 특히 객체의 속성에도 사용되므로 딕셔너리는 잘 알아두는 것이 좋습니다. 리스트 장에서는 위치 기반 사촌 구조를 다룹니다.

딕셔너리 생성

중괄호 안에 각 키와 값 사이에 콜론, 쌍 사이에 쉼표를 넣습니다. 키는 거의 항상 문자열입니다. 값은 무엇이든 될 수 있습니다. 숫자, 문자열, 다른 리스트, 심지어 다른 딕셔너리도 가능합니다.

딕셔너리 리터럴은 중괄호를 사용하고 key: value 문법을 씁니다. 키는 어떤 불변 타입(해시 가능한 타입)이든 될 수 있습니다: 문자열, 정수, 튜플. 값은 어떤 Python 객체든 될 수 있습니다. 딕셔너리는 삽입 순서를 보존하므로, 반복할 때 항목들이 추가된 순서대로 나타납니다.

딕셔너리 리터럴은 왼쪽에서 오른쪽으로 평가됩니다. 키는 해시 가능해야 하는데(내용이 바뀔 수 없어 Python이 분류하는 숫자가 유지되어야 함), str, int, tuple은 가능하지만 listdict는 불가능합니다. 값에는 그런 제한이 없습니다. 삽입 순서는 Python 3.7 이후로 언어 보장사항이므로 반복과 직렬화가 재현 가능합니다. 주의할 점: 단일 리터럴에서 중복 키는 오류를 발생시키지 않고, 마지막 값을 유지하고 이전 값을 버립니다.

python
player = {
    "name": "민지",
    "score": 87,
    "level": 5,
    "alive": True,
}
Juno딕셔너리 생성 중괄호, 각 키와 값 사이에 콜론, 쌍 사이에 쉼표. 키는 보통 문자열이고, 값은 무엇이든 될 수 있으며, 다른 딕셔너리도 가능합니다. 항목을 추가한 순서가 나중에 돌려받는 순서인데, 이것을 예상보다 훨씬 자주 활용하게 됩니다.
Juno딕셔너리 생성key: value 쌍이 있는 중괄호입니다. 키는 어떤 불변 타입(str, int, tuple)이든 될 수 있고, 값은 어떤 객체든 될 수 있습니다. 반복은 삽입 순서를 따르므로 순서가 중요할 때 딕셔너리를 사용해도 됩니다.
Juno딕셔너리 생성 키는 해시 가능해야 하므로 str, int, tuple은 가능하고 list, dict는 불가능합니다. 삽입 순서는 3.7 이후 보장되므로 출력에 의존할 수 있습니다. 중복 키 리터럴 함정을 주의하세요. 마지막 값을 조용히 가져가고 경고 없이 이전 값을 버립니다.

값 접근

대괄호 안에 키를 넣어서 값을 얻습니다. 키가 없으면 Python은 KeyError를 발생시킵니다. 키가 있는지 확실하지 않을 때는 .get()을 사용하세요. 충돌하는 대신 None을 반환하거나 지정한 기본값을 반환합니다.

대괄호 접근은 키가 없으면 KeyError를 발생시킵니다. .get(key)는 누락시 None을 반환합니다. .get(key, default)는 대신 기본값을 반환합니다. 키의 존재가 확실하지 않을 때는 .get()을 사용하세요. try/except로 접근을 래핑하는 것보다 더 안전하고 읽기 좋습니다.

대괄호 접근은 키를 해싱하고 일치하는 슬롯을 읽는 방식으로 평균 O(1)에 동작합니다(크기와 무관하게 일정한 비용). 누락시 KeyError를 발생시킵니다. .get(key, default=None)은 같은 조회를 하지만 누락시 발생 대신 기본값을 반환하므로 키의 존재가 불확실할 때 올바른 선택입니다. 대괄호 접근 전에 보호하려면 key in d도 O(1)이므로, 예/아니요만 필요할 때 in을 선택하고, 값이나 기본값이 필요할 때 .get()을 선택하세요.

python
player = {"name": "민지", "score": 87}

player["name"]    # "민지"
player["score"]   # 87
player["lives"]   # KeyError (키가 없음)
python
player.get("score")          # 87
player.get("lives")          # None (오류 없음, 기본값으로 None 반환)
player.get("lives", 3)       # 3   (키가 없으면 이 기본값 사용)

.get()은 키가 없을 수 있을 때 더 안전합니다:

python
count = inventory.get("arrows", 0)   # "arrows"가 딕셔너리에 없으면 0
Juno값 접근d["key"]는 값을 주지만, 그 키가 없으면 KeyError를 발생시킵니다. 확실하지 않을 때는 .get()을 사용하세요. 충돌하는 대신 None을 반환하거나, 넘긴 기본값을 반환합니다. 저는 이제 모든 것을 기본값으로 설정하고 누락된 키로 인한 프로그램 충돌이 없어졌습니다.
Juno값 접근 대괄호 접근은 누락시 `KeyError`를 발생시키고, `.get(key)`는 `None`을 반환하고, `.get(key, default)`는 넘긴 것을 반환합니다. 키가 없을 수 있을 때 `.get()`을 사용하세요. `try/except`로 접근을 래핑하는 것보다 깔끔합니다.
Juno값 접근d[key]와 `.get()`은 모두 O(1) 조회를 하고, 차이는 누락시 동작뿐입니다. 발생 대 기본값 반환. 예/아니요만 필요할 때 `key in d`를 사용하고, 값이나 기본값이 필요할 때 한 단계로 `.get()`을 사용하세요.

추가 및 업데이트

대괄호로 키에 할당합니다. 키가 이미 있으면 값이 바뀝니다. 없으면 새 항목이 생성됩니다. .update()를 사용하여 전체 다른 딕셔너리를 한 번에 병합합니다.

키에 할당하는 것은 평균 O(1)이고 두 작업을 모두 수행합니다. 키가 새로우면 항목을 만들고, 이미 있으면 값을 바꿉니다. .update()는 다른 딕셔너리나 키-값 쌍의 반복 가능 객체를 받아서 각 항목에 같은 할당을 적용하며, 공유 키는 덮어씁니다.

d[key] = value는 키를 해싱하고 한 단계의 평균 O(1) 작업으로 삽입 또는 덮어쓰므로, 별도의 "추가"와 "업데이트"는 없습니다. 할당이 둘 다 합니다. .update(other)other의 각 항목에 대해 같은 작업을 수행하며, 공유 키는 덮어씁니다. 원본을 변경하지 않고 병합하려면 | 연산자(Python 3.9+)가 새 딕셔너리를 반환하고 두 입력을 손대지 않으며, |=.update()처럼 제자리에서 변경합니다. 함수가 호출자가 여전히 가지고 있는 딕셔너리를 수정하면 안 될 때 |를 사용하세요.

python
player = {"name": "민지", "score": 87}

player["score"] = 92        # 기존 항목 업데이트
player["level"] = 5         # 새 키 추가
python
extras = {"level": 5, "alive": True}
player.update(extras)   # extras의 키로 추가/덮어쓰기
Juno추가 및 업데이트 대괄호로 키에 할당합니다. 이미 있으면 값이 바뀌고, 없으면 새 항목이 생깁니다. 한 가지 문법이 둘 다 처리하므로, 먼저 확인할 필요가 없습니다. `.update()`를 사용하여 전체 다른 딕셔너리를 한 번에 접으세요.
Juno추가 및 업데이트d[key] = value는 한 번에 만들거나 바꾸므로, 별도의 추가 대 업데이트는 없습니다. .update(other)는 전체 딕셔너리를 병합하며 공유 키는 덮어씁니다. 둘 다 키당 O(1)입니다.
Juno추가 및 업데이트 할당은 O(1) 단계로 삽입 또는 덮어쓰고, .update()는 항목마다 반복됩니다. 원본을 건드리고 싶지 않으면 |이 신선한 병합된 딕셔너리를 반환하고 |=는 제자리에서 변경합니다. 함수 내에서 `|`를 사용하여 호출자가 여전히 가진 딕셔너리를 조용히 바꾸지 마세요.

항목 제거

네 가지 제거 방법이 있습니다. .pop()은 키를 제거하고 값을 반환합니다. 기본값이 있는 .pop()은 키가 없을 수 있을 때 안전합니다. del은 반환값 없이 키를 제거합니다. .clear()는 전체 딕셔너리를 비웁니다.

.pop(key)는 누락시 KeyError를 발생시킵니다. .pop(key, default)는 대신 기본값을 반환하므로 안전한 제거 방법입니다. del d[key]는 반환값 없이 키를 제거하며 누락시 KeyError를 발생시킵니다. .clear()는 모든 항목을 제거하지만 딕셔너리 객체 자체는 유지합니다.

.pop(key, default)는 한 단계의 평균 O(1) 조회로 제거하고 반환하며, 기본값이 누락을 안전하게 만듭니다. 없으면 누락 키는 KeyError를 발생시킵니다. del d[key]는 반환값 없이 제거하며 누락시 발생시킵니다. .clear()는 딕셔너리를 비우지만 같은 객체를 유지하므로, 그것을 가리키는 다른 이름은 빈 딕셔너리를 봅니다. 기억할 실패 모드: 딕셔너리를 반복하는 동안 변경하면 루프 중간에 RuntimeError를 발생시킵니다. 해결책은 제거할 키를 for key in list(d):로 스냅샷한 후 원본에서 제거하는 것입니다.

python
player = {"name": "민지", "score": 87, "level": 5}

player.pop("level")            # "level"을 제거하고 5를 반환
player.pop("lives", None)      # 안전한 pop, 키가 없으면 None 반환
del player["score"]            # "score"를 제거, 반환값 없음
player.clear()                 # 모두 제거

.pop()에 기본값을 사용하는 것이 없을 수 있는 키를 제거하는 가장 안전한 방법입니다.

Juno항목 제거.pop(key)는 키를 제거하고 값을 반환하며, .pop(key, None)은 키가 없을 수 있을 때 침착합니다. del d[key]는 반환값 없이 제거하고, .clear()는 전부 비웁니다. .pop()의 기본값이 많은 `KeyError` 놀라움으로부터 저를 구했습니다.
Juno항목 제거.pop(key)는 누락시 발생시키고, .pop(key, default)는 발생시키지 않으므로 안전한 선택입니다. del d[key]는 반환값 없이 제거하며 누락시 발생시키고; .clear()는 딕셔너리를 비우지만 객체 자체는 유지합니다.
Juno항목 제거.pop(key, default)는 누락 안전 제거 및 반환입니다. del과 대괄호 pop은 누락 키에서 발생시킵니다. 물리는 함정: 반복하는 동안 제거하면 `RuntimeError`를 던집니다. 먼저 `for key in list(d):`로 스냅샷한 후 원본에서 삭제하세요.

반복

세 가지 뷰로 딕셔너리의 다른 부분을 반복할 수 있습니다. 딕셔너리를 직접 반복하면 키를 얻습니다. .values()는 값을 줍니다. .items()는 둘 다 한 번에 주며, 이것이 가장 많이 사용할 것입니다. 각 쌍을 두 이름으로 분해하여 깔끔하고 읽기 좋은 루프를 만드세요.

.keys(), .values(), .items()는 리스트가 아닌 뷰 객체를 반환합니다. 뷰는 딕셔너리의 현재 상태를 동적으로 반영합니다. 딕셔너리를 수정하면 뷰도 즉시 업데이트됩니다. 튜플 분해 for k, v in d.items()가 명확하게 읽혀 대부분의 루프에서 .items()가 가장 유용합니다.

.keys(), .values(), .items()뷰 객체(딕셔너리의 생생한 창으로, 별도 리스트가 아님)를 반환합니다. 뷰는 복사하지 않고 딕셔너리의 현재 상태를 반영하므로, 딕셔너리가 변경되면 뷰도 함께 변경됩니다. 키는 고유하고 해시 가능하므로 키 뷰는 집합 대수를 지원합니다. d.keys() & other.keys()는 공유 키를 찾고, -는 차이를 찾으므로 두 설정을 비교할 때 편합니다. 같은 반복 중 변경 함정이 적용됩니다. 반복하는 동안 딕셔너리를 반드시 변경해야 한다면 list(d.items())으로 스냅샷을 반복하세요.

python
player = {"name": "민지", "score": 87, "level": 5}

for key in player:               # 키 반복 (가장 흔함)
    print(key)

for key in player.keys():        # 같음, 명시적 키 뷰
    print(key)

for value in player.values():    # 값들
    print(value)

for key, value in player.items():   # 둘 다, 가장 유용함
    print(f"{key}: {value}")

.items()가 가장 많이 사용할 것입니다. 각 쌍을 두 이름으로 분해하면 루프가 읽기 좋아집니다.

Juno반복 딕셔너리를 직접 반복하면 키를 얻습니다. .values()는 값을 주고, .items()는 둘 다 주며, 이것이 가장 많이 사용할 것입니다. 각 쌍을 `for key, value in player.items()`처럼 두 이름으로 분해하면 루프가 훨씬 읽기 좋아졌습니다.
Juno반복 딕셔너리를 직접 반복하면 키를 얻고, .keys()는 같은 결과이고, .values()는 값을 주며, .items()는 둘 다 주며 가장 많이 사용할 것입니다. 튜플 분해 `for k, v in d.items()`이 가장 깔끔하게 읽힙니다.
Juno반복 세 뷰는 생생한 창으로, 복사본이 아니므로 딕셔너리가 변경되면 따라갑니다. 키 뷰는 집합 대수를 하므로 `d.keys() & other.keys()`가 두 딕셔너리를 한 줄에서 비교합니다. 제거와 같은 규칙: 반복하는 동안 변경하지 말고, 반드시 해야 한다면 `list(d.items())`로 스냅샷하세요.

멤버십 확인

in은 딕셔너리에 키가 있는지 확인합니다. 값을 확인하지 않고 키만 확인합니다. 무언가가 없는지 확인하려면 not in을 사용하세요.

innot in은 딕셔너리에서 O(1)이며 키만 확인합니다. 값을 확인하려면 in d.values()를 사용하는데, 값이 인덱싱되지 않으므로 O(n)입니다.

key in d는 키를 해싱하고 한 슬롯을 읽는 방식으로 O(1) 평균 작업을 수행하므로, 딕셔너리 크기와 무관하게 일정합니다. value in d.values()는 값을 하나하나 검토해야 하므로 O(n)이고, 비용이 크기와 함께 증가합니다. 그 비대칭이 값을 찾거나 검사하는 대신 키로 저장하는 전체 이유입니다. 값을 자주 검사하고 있다면, 아마 딕셔너리의 방향이 잘못된 것이거나 값을 키로 매핑하는 두 번째 딕셔너리가 필요할 것입니다.

python
player = {"name": "민지", "score": 87}

"name"  in player      # True
"lives" in player      # False
"lives" not in player  # True

in은 키만 확인합니다. 값을 확인하려면 in player.values()를 사용하면 되지만, 거의 필요하지 않습니다.

Juno멤버십 확인in은 키가 딕셔너리에 있는지 말해주며, 오직 키만입니다. 딕셔너리가 아무리 커도 빠릅니다. not in으로 뒤집어 무언가가 없는지 확인하세요.
Juno멤버십 확인innot in은 키만 O(1)로 확인합니다. 값을 확인하려면 `value in d.values()`이 필요하며, 이것은 O(n)이므로 값이 인덱싱되지 않습니다. 그래서 키로 찾을 수 있도록 설계하세요.
Juno멤버십 확인key in d는 O(1), value in d.values()는 O(n)입니다. 그 간격이 값을 검사하는 대신 키로 저장하는 이유입니다. 값을 계속 검사하고 있다면, 딕셔너리 방향이 잘못되었을 가능성이 높으므로 역 맵을 추가하세요.

중첩 딕셔너리

값은 그 자체로 딕셔너리일 수 있습니다. 이렇게 여러 수준의 구조화된 데이터를 표현합니다. 플레이어가 스탯 섹션을 가지거나, 설정 파일이 서브 섹션을 가질 때입니다. 중첩된 값에 대괄호 두 개로 접근합니다. 첫 번째는 외부 키를 선택하고, 두 번째는 내부 키를 선택합니다.

중첩 딕셔너리는 값이 그 자체로 딕셔너리인 딕셔너리입니다. 연결된 첨자로 접근합니다. 내부 딕셔너리를 변경하면 외부 딕셔너리에 영향을 미치는데, 외부 딕셔너리가 같은 객체에 대한 참조를 보유하기 때문입니다. 가능하면 중첩을 얕게 유지하세요. 깊은 중첩은 빠르게 읽고 탐색하기 어려워집니다.

중첩 딕셔너리는 내부 딕셔너리에 대한 참조를 보유합니다(참조는 한 공유 객체를 가리키는 포인터이므로, 두 이름이 같은 내부 딕셔너리를 가리킬 수 있음). 이것이 복사가 물리는 곳입니다. d.copy()얕은 복사로, 외부 딕셔너리를 복제하지만 내부 딕셔너리는 여전히 공유되므로, 복사를 통해 하나를 변경하면 원본도 변경됩니다. 복사가 완전히 독립적이어야 할 때 copy.deepcopy()는 전체 트리를 따라가며 모든 수준을 복제합니다. 설정을 변경할 수 있는 코드에 주기 전에 사용하세요. 각 대괄호 단계는 자체 O(1) 조회이므로 깊이는 속도에 비용이 없고 읽기 쉬움에만 비용이 있습니다.

python
users = {
    "alice": {"score": 87, "level": 5},
    "bob": {"score": 74, "level": 3},
}

users["alice"]["score"]   # 87
users["bob"]["level"]     # 3

연결된 대괄호로 접근합니다. 깊게 중첩된 구조의 경우 이것이 번거로워질 수 있으므로 가능하면 중첩을 얕게 유지하세요.

Juno중첩 딕셔너리 값이 전체 딕셔너리 그 자체일 수 있으며, 이렇게 플레이어와 스탯 섹션이 있는 구조화된 데이터를 보유합니다. 대괄호 두 개로 내부에 도달합니다. 첫 번째는 외부 키를 선택하고, 두 번째는 내부에서 선택합니다. 편리하지만 너무 깊이 중첩하면 읽기 투박해집니다.
Juno중첩 딕셔너리 중첩 딕셔너리는 값이 딕셔너리인 딕셔너리이며; 연결된 대괄호로 내부에 도달합니다. 외부 딕셔너리가 내부 딕셔너리에 대한 참조를 보유하므로 내부 딕셔너리를 변경하면 그것이 참조되는 곳 어디든 표시됩니다. 중첩을 얕게 유지하거나 탐색하기 어려워집니다.
Juno중첩 딕셔너리 외부 딕셔너리가 내부 딕셔너리에 대한 참조를 저장하므로 .copy()는 얕습니다. 복사본이 그 내부 딕셔너리를 공유하고 변경이 둘 다 새어나갑니다. 진정한 독립이 필요할 때 copy.deepcopy()를 사용하세요. 깊이는 속도에 무료이고 읽기 쉬움에 비싸므로 얕게 유지하세요.

setdefault

.setdefault()는 키가 있으면 읽고, 없으면 기본값으로 설정한 후 값을 반환합니다. 키가 존재하길 원하지만 이미 있으면 덮어쓰고 싶지 않을 때 유용합니다.

.setdefault(key, default)는 한 번에 읽거나 생성합니다. 키가 있으면 현재 값을 변경하지 않고 반환하고, 없으면 기본값을 삽입하고 반환합니다. 일반적인 사용 사례는 별도 존재 확인 없이 그룹화된 구조를 구축하는 것입니다.

.setdefault(key, default)는 한 단계의 O(1) 조회로 읽거나 생성합니다. 존재하는 키는 기존 값을 변경하지 않고 반환하고, 없는 키는 default를 삽입하고 반환합니다. 주목할 함정: default는 일반 인수이므로, 키가 이미 있어도 항상 구축됩니다. 그 기본값이 구축하기 비싸면(신선한 객체, 함수 호출), 모든 호출에서 비용을 지불합니다. 다음 섹션의 defaultdict는 누락에서만 팩토리를 실행하여 이를 회피합니다. 일반적인 "항목을 목록으로 그룹화" 패턴의 경우 .setdefault(key, []).append(...)key in d 확인을 대체하는 표준 한 줄입니다.

python
inventory = {}

inventory.setdefault("arrows", 0)    # "arrows": 0를 설정하고, 0을 반환
inventory.setdefault("arrows", 10)   # "arrows"가 이미 있으므로 변경 없음, 0 반환

키 존재를 먼저 확인하지 않고 그룹화된 구조를 구축하는 데 유용합니다:

python
groups = {}

for name, team in players:
    groups.setdefault(team, []).append(name)
Junosetdefault.setdefault(key, default)는 키가 있으면 읽고, 없으면 기본값으로 설정하고 반환합니다. 이미 있는 값을 망치지 않고 키가 존재하는지 확인하는 깔끔한 방법입니다. 저는 대부분 그룹화에 사용하며, "이 키가 여기 있나요?" 확인이 필요 없습니다.
Junosetdefault.setdefault(key, default)는 기존 값을 변경하지 않고 반환하거나, 누락시 기본값을 삽입하고 반환합니다. 보상은 그룹화입니다. `d.setdefault(k, []).append(x)`는 별도 존재 확인 없이 키당 목록을 구축합니다.
Junosetdefault 한 단계의 O(1) 읽기 또는 생성이지만 기본값은 항상 구축되므로 비싼 기본값은 모든 호출에서 비용이 듭니다. 그것이 정확히 `defaultdict`의 누락 팩토리가 고치는 것입니다. 그룹화의 경우 `.setdefault(k, []).append(x)`는 `key in d` 확인을 물리치는 한 줄짜리입니다.

collections.defaultdict 및 Counter

표준 라이브러리에는 일반적인 패턴을 자동으로 처리하는 두 가지 딕셔너리 서브클래스가 있습니다. defaultdict는 누락된 키에 대해 기본값을 만들므로 KeyError를 절대 얻지 못합니다. Counter는 시퀀스에서 각 항목이 나타나는 빈도를 세고 결과를 딕셔너리로 제공합니다.

defaultdict는 새 키에 대한 기본값을 생성하는 호출 가능 객체를 가지므로 .setdefault()가 필요 없습니다. Counter는 빈도 계산 전용 특화 딕셔너리이며 .most_common() 메서드가 있습니다. 둘 다 딕셔너리 서브클래스이므로 모든 표준 딕셔너리 작업이 작동합니다.

defaultdict(factory)는 처음 누락 키를 읽을 때 팩토리(list, int 같은 영인자 호출 가능 객체)를 실행하고 결과를 저장하고 반환하므로 d[k]는 절대 KeyError를 발생시키지 않습니다. 그것이 .setdefault()의 핵심 차이점입니다. 팩토리는 누락시에만 실행되므로 비싼 기본값은 흔한 경로에서 비용이 없습니다. 주목할 함정: 누락 키를 읽으면 만들어지므로 d[k]를 확인하면 딕셔너리를 자랄 수 있습니다. 삽입하지 않고만 테스트하고 싶으면 k in d를 사용하세요. Counter는 시퀀스를 세도록 구축된 dict입니다. 시퀀스를 먹으면 발생을 세고 .most_common(n)은 상위 n을 반환합니다. 둘 다 collections 모듈에 있습니다.

defaultdictCounter는 표준 라이브러리에 있으므로 먼저 가져와야 합니다. 가져오기는 모듈 장에서 완전히 다룹니다.

python
from collections import defaultdict

groups = defaultdict(list)
for name, team in players:
    groups[team].append(name)   # 팀이 새로우면 KeyError 없음
python
from collections import Counter

words = ["cat", "dog", "cat", "bird", "cat", "dog"]
counts = Counter(words)
# Counter({'cat': 3, 'dog': 2, 'bird': 1})

counts.most_common(2)   # [('cat', 3), ('dog', 2)]

Counter는 많은 "루프에서 것을 센다" 보일러플레이트를 절약합니다.

Junocollections.defaultdict 및 Counterdefaultdict는 누락된 키에 대해 기본값을 채우므로 그룹화와 계산이 더 이상 `KeyError`를 던지지 않습니다. Counter는 시퀀스에서 각 항목이 얼마나 자주 나타나는지 세고 딕셔너리로 반환합니다. 처음 손으로 쓴 계산 루프를 `Counter`로 바꿨을 때, 제 코드의 절반이 사라졌습니다.
Junocollections.defaultdict 및 Counterdefaultdict(list) 또는 defaultdict(int)는 새 키에서 기본값을 자동 생성하므로 그룹화와 계산의 경우 `.setdefault()`를 폐기합니다. Counter는 빈도를 위해 조정된 딕셔너리이며 `most_common(n)`이 내장되어 있습니다. 둘 다 딕셔너리 서브클래스이므로 아는 모든 것이 여전히 작동합니다.
Junocollections.defaultdict 및 Counterdefaultdict의 팩토리는 누락시에만 실행되므로 `.setdefault()`와 달리 비싼 기본값은 흔한 경로에서 비용이 없습니다. 함정: 누락 키를 읽으면 만들어지므로 `d[k]`가 아닌 `k in d`로 테스트하세요. Counter는 시퀀스를 세고 `.most_common(n)`이 상위 n을 뽑습니다.

실제 사용

점수 추적 도구를 구축하고 모든 항목이 포함된 요약을 인쇄합니다:

python
scores = {"민지": 87, "준호": 74, "예은": 92, "경준": 55}

total = sum(scores.values())
average = total / len(scores)

print(f"플레이어:  {len(scores)}")
print(f"평균:  {average:.1f}")
print(f"최고:  {max(scores.values())}")
print(f"최저:   {min(scores.values())}")
print()

for name, score in scores.items():
    print(f"  {name}: {score}")

루프에서 파일당 결과 딕셔너리를 구축한 후 모든 항목에 걸쳐 요약합니다:

python
job_results = {}
files = ["report_jan.csv", "report_feb.csv", "report_mar.csv"]

for filename in files:
    size = len(filename) * 100   # 실제 파일 크기를 위한 자리 표시자
    if size < 2000:
        status = "ok"
    else:
        status = "large"
    job_results[filename] = {"size": size, "status": status}

ok_count = 0
large_count = 0

for result in job_results.values():
    if result["status"] == "ok":
        ok_count += 1
    else:
        large_count += 1

print(f"처리됨 {len(job_results)} 파일: {ok_count} ok, {large_count} large")

필수 필드를 반복하여 중첩 요청 딕셔너리를 검증한 후 특성 중요도 딕셔너리를 제자리에서 정규화합니다:

python
request = {
    "method": "POST",
    "path": "/users",
    "headers": {"Content-Type": "application/json"},
    "body": {"username": "alice", "email": "[email protected]"},
}

body = request["body"]
errors = []

for field in ["username", "email"]:
    if not body.get(field):
        errors.append(f"필수 필드 누락: {field}")

if "email" in body and "@" not in body["email"]:
    errors.append("잘못된 이메일 형식")

print(f"메서드: {request['method']} {request['path']}")
if errors:
    print(f"오류: {errors}")
else:
    print("검증 통과")

# 특성 중요도 값을 1로 합하도록 정규화
feature_importance = {"age": 0.34, "income": 0.28, "region": 0.15, "purchases": 0.23}
total = sum(feature_importance.values())

for key in feature_importance:
    feature_importance[key] = round(feature_importance[key] / total, 3)

print(f"정규화됨: {feature_importance}")