16. Từ điển và Bộ

Từ điển và bộ là các vùng chứa bảng băm chính của CPython. Một từ điển ánh xạ các khóa tới các giá trị. Một bộ lưu trữ các khóa không có giá trị liên quan.

Chúng được sử dụng ở mọi nơi trong Python:```text id="cp7dx7" module globals class namespaces instance attributes keyword arguments function annotations import caches memoization tables membership indexes deduplication sets


## 16.1 Từ điển Ngữ nghĩa

Mt t đin ánh x các khóa có th băm thành các giá tr.```python id="cvrlq9"
d = {
    "name": "Ada",
    "age": 36,
}

print(d["name"])    # Ada
```Khóa phi có th băm được. Giá tr có th là bt k đối tượng nào.```python id="mb4tqx"
d = {}

d["x"] = []
d[42] = object()
d[(1, 2)] = "tuple key"
```Mt khóa có th băm được khi nó có giá tr băm n định và h tr so sánh đẳng thc tương thích vi hàm băm đó.```python id="100k2v"
hash("name")        # works
hash((1, 2))        # works
hash([1, 2])        # TypeError
```Danh sách không th băm được vì ni dung ca chúng có th thay đổi.

## 16.2 Đặt ngữ nghĩa

Mt b lưu tr các phn t có th băm duy nht.```python id="a9i1rs"
seen = set()

seen.add("x")
seen.add("x")

print(seen)         # {'x'}
```Mt tp hp rt hu ích khi bn cn kim tra tư cách thành viên, loi b trùng lp hoc tp hp đại s.```python id="utkml3"
a = {"red", "green", "blue"}
b = {"green", "yellow"}

print(a & b)        # intersection
print(a | b)        # union
print(a - b)        # difference
```MT`frozenset`là bt biến và có th băm được nếu tt c các phn t đều có th băm được.```python id="8f3u4m"
key = frozenset(["read", "write"])
d = {key: "permissions"}
```## 16.3 Bảng băm

T đin và b là bng băm.

Bng băm s dng giá tr băm để tìm v trí ca mt đối tượng trong bng ni b.

Để tra cu t đin:```text id="kobvyn"
hash key
find candidate slot
compare stored key with lookup key
return value if equal
```Đối vi thành viên được thiết lp:```text id="hbs3i1"
hash element
find candidate slot
compare stored element with lookup element
return present or absent
```Tra cu trường hp trung bình là thi gian không đổi:```text id="qme3op"
d[key]      average O(1)
key in d    average O(1)
x in set    average O(1)
```Hành vi trong trường hp xu nht có th suy gim khi nhiu khóa va chm, nhưng CPython bao gm kh năng x lý va chm và bo v ngu nhiên hàm băm.

## 16.4 Hợp đồng băm và bình đẳng

Hp đồng băm là:```text id="u4q6w3"
if a == b, then hash(a) == hash(b)
```Không cn phi làm ngược li:```text id="2ibsgd"
if hash(a) == hash(b), a may still be different from b
```Xung đột xy ra khi hai đối tượng không bng nhau có cùng hàm băm.

T đin x lý xung đột bng cách so sánh các khóa.

Ví d v khóa tùy chnh:```python id="7h6xvk"
class Key:
    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        return isinstance(other, Key) and self.value == other.value

    def __hash__(self):
        return hash(self.value)
```Nếu bn xác định`__eq__`, xác định mt tương thích`__hash__`ch khi đối tượng đủ bt biến để băm.

## 16.5 Khóa có thể thay đổi rất nguy hiểm

Hàm băm ca khóa phi duy trì n định trong khi khóa nm trong t đin hoc b.

Điu này tht t:```python id="51z0wo"
class BadKey:
    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        return isinstance(other, BadKey) and self.value == other.value

    def __hash__(self):
        return hash(self.value)

k = BadKey("a")
d = {k: "value"}

k.value = "b"
```Sau khi đột biến, đối tượng có th không còn tìm thy trong bng na.```python id="u2wkp9"
print(d.get(k))     # may fail unexpectedly
```T đin đặt khóa theo hàm băm cũ. Tra cu s dng hàm băm mi.

S dng d liu khóa bt biến.```python id="mzfzuj"
key = ("user", 123)
```## 16.6 Bố cục đối tượng từ điển

Đối tượng t đin CPython lưu tr siêu d liu và tr đến d liu bng.

V mt khái nim:```text id="36zah2"
PyDictObject
    object header
    used count
    version tag
    keys table pointer
    values pointer for split-table dicts
```B cc bên trong được ti ưu hóa và dành riêng cho tng phiên bn, nhưng ý tưởng chính là:```text id="kpjby5"
dictionary object
    controls a hash table
    stores keys and values
    resizes as needed
```T đin không lưu tr các đối tượng khóa và giá tr ni tuyến dưới dng giá tr thô. Nó lưu tr các tham chiếu đến các đối tượng Python.```text id="jxgd18"
dict entry
    key pointer   ---> Python object
    value pointer ---> Python object
    hash value
```## 16.7 Bảng kết hợp và chia tách

T đin CPython có th s dng các b cc ni b khác nhau.

Mt bng kết hp lưu tr các khóa và giá tr cùng nhau.```text id="kswrs2"
entry
    hash
    key pointer
    value pointer
```Bng phân chia tách các khóa dùng chung khi các giá tr theo tng phiên bn.

Điu này rt hu ích cho các t đin ví d ca các đối tượng cùng lp.

Ví d:```python id="pr3ahp"
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

a = Point(1, 2)
b = Point(3, 4)
```C hai trường hp đều có thuc tính`x`Và`y`.

CPython có th chia s siêu d liu chính cho b cc phiên bn ca lp trong khi mi phiên bn lưu tr các giá tr riêng.

V mt khái nim:```text id="4gdmgx"
shared keys table
    "x"
    "y"

instance a values
    1
    2

instance b values
    3
    4
```Điu này làm gim mc s dng b nh cho nhiu phiên bn có cùng tên thuc tính.

## 16.8 Thứ tự chèn

T đin gi nguyên th t chèn.```python id="jf7ccx"
d = {}
d["a"] = 1
d["b"] = 2
d["c"] = 3

print(list(d))      # ['a', 'b', 'c']
```Cp nht khóa hin có s không di chuyn khóa đó:```python id="2d4s8h"
d["b"] = 20
print(list(d))      # ['a', 'b', 'c']
```Vic xóa và chèn li s to ra v trí chèn mi:```python id="2tmw4q"
del d["b"]
d["b"] = 200

print(list(d))      # ['a', 'c', 'b']
```Th t chèn là hành vi ngôn ng cho t đin trong Python hin đại, không ch đơn thun là chi tiết CPython ngu nhiên.

## 16.9 Tra cứu từ điển

Tra cu t đin có nhiu giai đon.```python id="fq8zvc"
value = d[key]
```V mt khái nim:```text id="5w58mp"
compute hash(key)
choose initial table slot
inspect slot
    if empty: key missing
    if stored hash differs: continue probing
    if stored key is key: found
    if stored key == key: found
    otherwise continue probing
```Danh tính được kim tra trước s bình đẳng khi có th. Nếu s dng cùng mt đối tượng làm khóa, vic tra cu có th tránh được công vic bình đẳng.```python id="b3vf1x"
key = "name"
d = {key: "Ada"}

print(d[key])
```Đối vi chui, hàm băm và ni b được lưu trong b nh đệm có th giúp vic tra cu đặc bit nhanh chóng.

## 16.10 Xử lý va chạm

Các khóa khác nhau có th có cùng hàm băm.```python id="u61im9"
class Collide:
    def __init__(self, value):
        self.value = value

    def __hash__(self):
        return 1

    def __eq__(self, other):
        return isinstance(other, Collide) and self.value == other.value
```Tt c các phiên bn băm thành`1`.

```python id="sn5hoa"
d = {}
for i in range(100):
    d[Collide(i)] = i
```T đin vn hot động nhưng hiu sut b nh hưởng vì cn nhiu thăm dò và kim tra tính bng nhau.

Tính năng ngu nhiên hóa hàm băm giúp bo v các khóa ging chui khi các cuc tn công xung đột có th d đoán được trong các chương trình đối mt vi mng.

## 16.11 Thay đổi kích thước

T đin s thay đổi kích thước khi nó quá đầy.

Mc tiêu là duy trì vic tra cu hiu qu bng cách duy trì khong trng trong bng.

V mt khái nim:```text id="km324o"
insert key
    if table load is high:
        allocate larger table
        reinsert active entries
    insert new key
```Thay đổi kích thước chi phí`O(n)`ti thi đim điu đó xy ra, nhưng tính trung bình, các thao tác chèn vn có hiu qu.

V mt tinh thn, điu này tương t như vic phân b quá mc trong danh sách. Vùng cha s dng thêm b nh để gi cho các hot động chung được nhanh chóng.

## 16.12 Xóa và nhập giả

Vic xóa khóa t đin không phi lúc nào cũng đánh du mt v trí là trng.

Ti sao?

Bi vì vic tra cu có th da vào chui thăm dò. Nếu vic xóa làm đứt chui, các khóa sau này có th không th truy cp được.

Vì vy các bng băm thường s dng các đim đánh du gi cho các v trí đã b xóa.

V mt khái nim:```text id="o8jbeg"
active slot
    contains key and value

empty slot
    never used

dummy slot
    used before, now deleted
```Vic chèn có th s dng li các khe gi. Tra cu phi b qua chúng.

Quá nhiu khe gi có th nh hưởng đến hiu sut, do đó vic thay đổi kích thước hoc xây dng li có th làm sch chúng.

## 16.13 Lượt xem từ điển

Đối tượng dng xem t đin hin th dng xem động ca khóa, giá tr hoc mc.```python id="ovth2c"
d = {"a": 1, "b": 2}

keys = d.keys()
values = d.values()
items = d.items()
```Lượt xem phn ánh các đột biến sau này:```python id="bjim9l"
keys = d.keys()

d["c"] = 3

print(keys)     # dict_keys(['a', 'b', 'c'])
```Lượt xem tránh sao chép. Chúng là nhng vt th nh tr li t đin.

Để to nh chp nhanh, hãy sao chép rõ ràng:```python id="k65165"
keys_snapshot = list(d.keys())
```## 16.14 Lặp lại một từ điển

Lp li mt t đin mang li các khóa.```python id="ra7i88"
for key in d:
    print(key)
```Tương đương:```python id="4hpn52"
for key in d.keys():
    print(key)
```Trong quá trình lp li, vic thay đổi kích thước ca t đin s gây ra li:```python id="4085f6"
for key in d:
    d["new"] = 1      # RuntimeError
```Vic thay đổi giá tr mà không thay đổi khóa thường được cho phép:```python id="t0n9jz"
for key in d:
    d[key] += 1
```Trình vòng lp da vào cu trúc t đin để luôn nht quán.

## 16.15 Phiên bản từ điển Tags

T đin CPython có th duy trì thông tin phiên bn thay đổi khi t đin thay đổi.

Th phiên bn rt hu ích cho vic ti ưu hóa thi gian chy, đặc bit là b đệm.

Ví d: tra cu thuc tính và tra cu toàn cc có th lưu vào b đệm thông tin khi t đin không thay đổi.

V mt khái nim:```text id="pfdsrf"
dict version = 100
cache lookup result for name "x"

later:
    if dict version still 100:
        cached result may be valid
    else:
        redo lookup
```Đây là mt công c chính để tăng tc độ tra cu động mà không làm thay đổi ng nghĩa Python.

## 16.16 Toàn cầu là từ điển

Không gian tên chung ca mô-đun là mt t đin.```python id="h0m5e5"
x = 10

globals()["x"] = 20

print(x)        # 20
```Khi mã Python đọc mt biến toàn cc, CPython thc hin tra cu t đin trong các phn tng quát ca mô-đun, vi d phòng cho các phn dng sn.

V mt khái nim:```text id="v5m31f"
LOAD_GLOBAL name
    look in globals dict
    if missing, look in builtins dict
```Bi vì toàn cu là t đin, nên vic truy cp biến toàn cc ph thuc vào hiu sut ca lnh. CPython s dng b đệm ni tuyến và th phiên bn để tăng tc đường dn này.

## 16.17 Thuộc tính sơ thẩm thường là từ điển

Các th hin đối tượng thông thường thường lưu tr các thuc tính trong t đin.```python id="zdzzxk"
class User:
    pass

u = User()
u.name = "Ada"
u.age = 36

print(u.__dict__)
```Đầu ra:```text id="ijc7p2"
{'name': 'Ada', 'age': 36}
```Truy cp thuc tính:```python id="6pnbk6"
u.name
```thường tr thành tra cu t đin, sau khi kim tra b mô t và tra cu cp độ loi.

CPython ti ưu hóa điu này rt nhiu vi t đin phân tách, khóa chia s và b nh đệm thuc tính.

## 16.18 Không gian tên lớp là từ điển

Ni dung lp thc thi trong ánh x không gian tên.```python id="d3f5st"
class User:
    species = "human"

    def greet(self):
        return "hello"
```Không gian tên lp thu thp:```text id="y3ded7"
species
greet
__module__
__qualname__
```Sau đó siêu d liu s to đối tượng lp t không gian tên đó.

Lp kết qu có ánh x các thuc tính ging như t đin.```python id="mtwgd9"
print(User.__dict__)
```T đin lp được hin th thông qua chế độ ch đọc`mappingproxy`xem.

## 16.19`mappingproxy`MỘT`mappingproxy`là chế độ xem chỉ đọc của bản đồ.```python id="t40d69"
class C:
    x = 1

print(C.__dict__)
```Bn không th ch định trc tiếp thông qua proxy ánh x:```python id="a5po6e"
C.__dict__["y"] = 2      # TypeError
```Nhưng bn có th gán thông qua cú pháp thuc tính lp thông thường:```python id="j36rbw"
C.y = 2
```Proxy ngăn chn s đột biến trc tiếp ca t đin lp ni b trong khi vn cho phép xem xét ni tâm.

## 16.20 Đối số từ khóa

Đối s t khóa được biu din bng cơ chế ging như ánh x.```python id="7yetex"
def f(**kwargs):
    print(kwargs)

f(a=1, b=2)
```Bên trong hàm,`kwargs`là mt cun t đin.

Vic truyn đối s t khóa ph thuc vào hiu sut vì các cuc gi là ph biến.

CPython có các quy ước gi chuyên dng để tránh vic to t đin không cn thiết khi có th, nhưng khi`**kwargs`là cn thiết, mt t đin được hin thc hóa.

## 16.21 Bộ cũng sử dụng bảng băm

Mt b tương t như mt t đin không có giá tr.```python id="j5dt5t"
s = {"a", "b", "c"}
```V mt khái nim:```text id="1c2tu2"
set table entry
    hash
    key pointer
```Thành viên:```python id="gc0h7j"
"x" in s
```s dng hàm băm và thăm dò, ging như tra cu dict.

Đặt thay đổi kích thước và x lý xung đột tương t như trong khái nim.

## 16.22 Thiết lập hoạt động

Các thao tác thiết lp s dng tư cách thành viên bng băm trong ni b.```python id="d85y3h"
a = {1, 2, 3}
b = {3, 4, 5}

print(a & b)    # {3}
print(a | b)    # {1, 2, 3, 4, 5}
print(a - b)    # {1, 2}
```Hiu sut ph thuc vào kích thước đầu vào.

Đối vi giao đim, tt hơn là lp li tp hp nh hơn và kim tra tư cách thành viên trong tp hp ln hơn.

V mt khái nim:```text id="s5h98o"
intersection(a, b)
    for each item in smaller set:
        if item in larger set:
            add to result
```Vic trin khai CPython s dng các loi chiến lược nhn biết kích thước này.

## 16.23 Đặt thứ tự lặp lại

Các b không ha hn th t chèn.```python id="fmu4b8"
s = {"a", "b", "c"}
print(list(s))
```Th t có th ph thuc vào hàm băm, kích thước bng, lch s chèn và ngu nhiên hàm băm thi gian chy.

Không viết mã ph thuc vào th t lp đã đặt.

Nếu th t quan trng, hãy s dng t đin làm bn đồ thành viên được sp xếp theo th t hoc sp xếp mt cách rõ ràng.```python id="jc3ogv"
ordered_unique = list(dict.fromkeys(items))
```## 16,24`dict.fromkeys`Một mô hình chống trùng lặp phổ biến:```python id="0og08r"
items = ["b", "a", "b", "c", "a"]

unique = list(dict.fromkeys(items))

print(unique)   # ['b', 'a', 'c']
```Điu này s dng th t chèn t đin để duy trì ln xut hin đầu tiên.

Mt b s b trùng lp nhưng mt th t:```python id="nozz18"
set(items)
```S dng vùng cha có ng nghĩa phù hp vi yêu cu.

## 16.25 Giá trị mặc định và khóa bị thiếu

Mt s API t đin x lý các khóa b thiếu.```python id="860ztd"
d.get("key", default)

gettrả về giá trị mặc định mà không cần chèn.```python id="kkfpw8" d.setdefault("key", [])


`setdefault`chèn mặc định nếu thiếu khóa.

Mẫu chung:```python id="t4jd1i"
groups = {}

for item in items:
    groups.setdefault(item.category, []).append(item)
```Để nhóm lặp lại,`collections.defaultdict`thường  ràng hơn:```python id="27x3y1"
from collections import defaultdict

groups = defaultdict(list)

for item in items:
    groups[item.category].append(item)
```## 16.26 Hiểu Từ điển

Việc hiểu từ điển xây dựng một từ điển từ một lần lặp.```python id="uxw8zk"
squares = {x: x * x for x in range(10)}
```Nếu các khóa trùng lặp xuất hiện, các giá trị sau sẽ thay thế các giá trị trước đó trong khi vị trí chèn của khóa đầu tiên được giữ lại.```python id="yct6sa"
d = {x % 2: x for x in range(5)}

print(d)        # {0: 4, 1: 3}
```Chìa khóa`0``1`được chèn sớm, sau đó các giá trị được cập nhật.

## 16.27 Đặt mức độ hiểu

Sự hiểu biết về tập hợp sẽ xây dựng một tập hợp.```python id="3sgagh"
mods = {x % 10 for x in range(100)}
```Bản sao sụp đổ.

Việc hiểu tập hợp rất hữu ích cho việc trích xuất các giá trị được tính toán duy nhất.```python id="n3vtrw"
domains = {url.split("/")[2] for url in urls}
```Một lần nữa, thứ tự lặp lại không phải  sự đảm bảo về mặt ngữ nghĩa.

## 16.28 Hash ngẫu nhiên

CPython sử dụng ngẫu nhiên hàm băm cho một số loại  thể băm như chuỗi  byte.

Điều này  nghĩa  giá trị băm  thể khác nhau giữa các quy trình.```python id="og1g48"
print(hash("name"))
```Chạy chương trình hai lần  giá trị  thể khác nhau.

Đừng kiên trì`hash()`các giá trị xuyên suốt các tiến trình. Không sử dụng chúng làm ID ổn định.

Để băm ổn định, hãy sử dụng hàm băm được xác định:```python id="0clx65"
import hashlib

digest = hashlib.sha256(b"name").hexdigest()

hash()dành cho các bảng băm trong quá trình, không phải là danh tính bền vững.

16.29 Các mẫu hiệu suất phổ biến

Nhiệm vụ Thùng chứa Lý do
Tra cứu khóa-giá trị dict Tra cứu bảng băm
Kiểm tra tư cách thành viên set Trung bìnhO(1)
Bảo toàn các giá trị duy nhất đầu tiên dict.fromkeys Chìa khóa đã đặt hàng
Đếm vật phẩm collections.Counter Lớp con dict chuyên biệt
Mục nhóm defaultdict(list) Tránh việc kiểm tra thiếu chìa khóa lặp đi lặp lại
Phím tổng hợp cố định tuple Có thể băm nếu các phần tử có thể băm
Sắp xếp các phím theo thứ tự sorted(d) Đặt hàng rõ ràng
Chế độ xem không gian tên lớp chỉ đọc mappingproxy Ngăn chặn đột biến trực tiếp

##16.30 Những lỗi thường gặp

Sử dụng danh sách thành viên thường xuyên:python id="89pu6v" if user_id in user_ids: ... Nếu nhưuser_idslớn và việc tra cứu diễn ra thường xuyên, hãy sử dụng một bộ.python id="2yd104" user_ids = set(user_ids) Sử dụng các đối tượng có thể thay đổi làm khóa khái niệm:python id="wotz0s" key = [1, 2] d[key] = "value" # TypeError Sử dụng một bộ dữ liệu:python id="lpvy37" key = (1, 2) d[key] = "value" Tùy theo thứ tự đã đặt:```python id="hmuq1q" first = next(iter(my_set))


kiên trì`hash()`:

```python id="pchqlg"
id_value = hash(username)
```Thay vào đó hãy sử dụng hàm băm ổn định.

## 16.31 Bản phác thảo API C

Tạo từ điển:```c id="eclt1e"
PyObject *d = PyDict_New();
if (d == NULL) {
    return NULL;
}
```Thiết lập một mục:```c id="lk7i25"
PyObject *key = PyUnicode_FromString("name");
PyObject *value = PyUnicode_FromString("Ada");

if (key == NULL || value == NULL) {
    Py_XDECREF(key);
    Py_XDECREF(value);
    Py_DECREF(d);
    return NULL;
}

if (PyDict_SetItem(d, key, value) < 0) {
    Py_DECREF(key);
    Py_DECREF(value);
    Py_DECREF(d);
    return NULL;
}

Py_DECREF(key);
Py_DECREF(value);

PyDict_SetItemkhông ăn cắp tài liệu tham khảo. Nó tăng những gì nó lưu trữ. Người gọi phải giải phóng các tài liệu tham khảo thuộc sở hữu của mình.

Nhận một mục có thể trả về một tài liệu tham khảo đã mượn tùy thuộc vào API:```c id="m18ydq" PyObject *value = PyDict_GetItemWithError(d, key); if (value == NULL) { if (PyErr_Occurred()) { return NULL; } Py_RETURN_NONE; }

/* borrowed reference */ Py_INCREF(value); return value;


## 16.32 Đặt bản phác thảo API C

Tạo một bộ:```c id="l7rkf0"
PyObject *s = PySet_New(NULL);
if (s == NULL) {
    return NULL;
}
```Thêm một mục:```c id="gjqb0m"
PyObject *item = PyUnicode_FromString("x");
if (item == NULL) {
    Py_DECREF(s);
    return NULL;
}

if (PySet_Add(s, item) < 0) {
    Py_DECREF(item);
    Py_DECREF(s);
    return NULL;
}

Py_DECREF(item);
```Bộ sở hữu tham chiếu riêng sau khi chèn. Tài liệu tham khảo địa phương vẫn được phát hành.

Thành viên:```c id="b4gg5h"
int present = PySet_Contains(s, item);
if (present < 0) {
    return NULL;
}
```Kết quả là:```text id="eari3m"
1 if present
0 if absent
-1 on error
```## 16.33 Mô hình tinh thần

Sử dụng mô hình này:```text id="78tgbw"
dict
    hash table mapping keys to values
    keys must be hashable
    insertion order preserved
    stores references to keys and values
    used throughout CPython runtime

set
    hash table storing keys only
    keys must be hashable
    no insertion-order guarantee
    optimized for membership and set algebra
```Đối với cả hai container:```text id="e36bns"
hash chooses where to look
equality confirms the key
collisions are handled by probing
resizing keeps lookup efficient
mutation changes table structure
iteration depends on table state
```## 16.34 Tóm tắt

Từ điển và bộ là các thùng chứa bảng băm. Từ điển ánh xạ các khóa có thể băm thành các giá trị và giữ nguyên thứ tự chèn. Các bộ lưu trữ các phần tử có thể băm duy nhất và tối ưu hóa việc kiểm tra tư cách thành viên cũng như các hoạt động thiết lập.

CPython sử dụng từ điển làm cấu trúc thời gian chạy cốt lõi cho toàn cục, thuộc tính, lớp, mô-đun, đối số từ khóa và bộ đệm. Việc triển khai của chúng bao gồm bộ nhớ đệm băm, xử lý xung đột, thay đổi kích thước, bố cục bảng phân chia, thẻ phiên bản và bảo toàn đơn hàng.

Việc hiểu từ điển và bộ giải thích phần lớn tốc độ, mô hình đối tượng, tra cứu thuộc tính, hành vi vùng tên và các mẫu hiệu suất phổ biến của Python.