Hướng dẫn how dictionary works under the hood in python - cách từ điển hoạt động dưới mui xe trong python

Mục lục

Bạn có phải là nhà phát triển Python mong muốn tìm hiểu thêm về nội bộ của ngôn ngữ và để hiểu rõ hơn về cách các bảng băm python và cấu trúc dữ liệu hoạt động? Hoặc có thể bạn có kinh nghiệm trong các ngôn ngữ lập trình khác và muốn hiểu làm thế nào và các bảng băm được thực hiện trong Python? Bạn đã đến đúng nơi!

Show

Đến cuối bài viết này, bạn sẽ có một sự hiểu biết về:

  • Bảng băm là gì
  • Làm thế nào và ở đâu các bảng băm được thực hiện trong Python
  • Làm thế nào hàm hàm băm hoạt động
  • Những gì xảy ra dưới mui xe trong từ điển
  • Những ưu và nhược điểm của bàn băm ở Python
  • Từ điển Python nhanh như thế nào
  • Cách băm các lớp tùy chỉnh của bạn

Hướng dẫn này nhằm vào các nhà phát triển Python trung gian và thành thạo. Nó giả định sự hiểu biết cơ bản về từ điển và bộ Python.

Hãy cùng đi sâu vào việc nhìn vào bàn băm Python!

Học những điều cơ bản của bảng băm

Trước khi đi sâu vào các chi tiết triển khai Python, trước tiên bạn cần hiểu các bảng băm là gì và cách sử dụng chúng.

Bảng băm là gì

Bạn đã bao giờ nghĩ về cách lưu trữ từ điển Python trong bộ nhớ chưa? Bộ nhớ trong máy tính của chúng tôi có thể được coi là một mảng đơn giản với các chỉ mục số:

Hướng dẫn how dictionary works under the hood in python - cách từ điển hoạt động dưới mui xe trong python

Vậy làm thế nào mà Python biết giá trị nào thuộc về khóa nào, khi sử dụng các khóa không số? Giải pháp đơn giản nhất là có mỗi phần tử trong mảng lưu trữ cả khóa và giá trị, lặp qua mảng và kiểm tra phần tử theo phần tử, cho đến khi tìm một phần chứa khóa chứa khóa mong muốn. Giải pháp này không phải là hiệu quả hoàn hảo, vì nó sẽ yêu cầu lặp đi lặp lại qua mảng nhiều lần - và đây là nơi các bảng băm đến chơi.

Bảng băm là một cấu trúc được thiết kế để lưu trữ một danh sách các cặp giá trị khóa, mà không ảnh hưởng đến tốc độ và hiệu quả của việc thao túng và tìm kiếm cấu trúc.

Bảng băm sử dụng hàm băm để tính toán một chỉ mục, thành một mảng các khe cắm, từ đó có thể tìm thấy giá trị mong muốn.hash function to compute an index, into an array of slots, from which the desired value can be found.

Làm thế nào để một hàm băm hoạt động

Chức năng băm, ở dạng đơn giản nhất, là một hàm tính toán chỉ mục của cặp giá trị khóa-để bạn có thể nhanh chóng chèn, tìm kiếm và xóa các phần tử trong mảng bộ nhớ của bạn.

Bạn nên bắt đầu với một ví dụ đơn giản: một đối tượng giống như từ điển, chứa 3 sản phẩm và số lượng hàng tồn kho của chúng.

{"avocados": 10, "oranges": 4, "apples": 2}

Đối với hàm băm, bạn sẽ cần một phương thức để biến các phím chuỗi thành các giá trị số để bạn có thể nhanh chóng tra cứu chúng trong bộ nhớ.

Hãy tự mình thử nó

Cố gắng nghĩ về một thao tác đơn giản để thực hiện trên các giá trị chuỗi để biến chúng thành các giá trị số.

Hiển thị giải pháp

Có nhiều phương pháp để đạt được điều đó. Làm thế nào về việc tính độ dài của mỗi khóa?

Vì vậy, bạn đã quyết định tính toán độ dài của các giá trị chuỗi, tuyệt vời! Don Tiết quên rằng các giá trị số phải nằm trong vòng 0 đến 3 để chúng phù hợp với bên trong mảng 3 phần tử. Bạn có thể sử dụng toán tử modulo về độ dài cho điều đó:

length of the key % table_size (3)

Ví dụ,

>>> hash("hash")
832529968546820528

>>> # different run
>>> hash("hash")
8049792375956551724
0 có 8 chữ cái, do đó nó sẽ được đặt trong chỉ số thứ 2. Đây là mảng cuối cùng:

Hướng dẫn how dictionary works under the hood in python - cách từ điển hoạt động dưới mui xe trong python

Tiếng hoan hô! Bạn chỉ cần xây dựng bàn băm đầu tiên của bạn! Giữ trong một giây ở đó. Điều gì xảy ra khi hai phím có cùng chiều dài? Bạn đã thắng được có thể chèn cả hai ở cùng một chỉ số. Điều này được gọi là va chạm băm.hash collision.

Cách xử lý va chạm

Va chạm băm thực tế là không thể tránh khỏi khi băm một tập hợp ngẫu nhiên của một tập hợp lớn các phím có thể. Có nhiều chiến lược để xử lý va chạm - tất cả đều yêu cầu các khóa được lưu trữ trong bảng, cùng với các giá trị liên quan. Hai trong số những cái phổ biến nhất là địa chỉ mở và chuỗi riêng biệt.Open Addressing and Separate Chaining.

  • Địa chỉ mở: Khi xảy ra va chạm băm, thuật toán này tiến hành theo một số trình tự thăm dò cho đến khi tìm thấy một khe cắm không có người ở. Startegy này bao gồm lưu trữ tất cả các bản ghi trong một mảng duy nhất. Ví dụ, việc thực hiện đơn giản chiến lược này sẽ là tiến hành thêm một yếu tố mỗi khi chỉ số được tính toán đã được chiếm, cho đến khi tìm thấy một điểm không có người ở. Việc thực hiện này được gọi là thăm dò tuyến tính. When a hash collision occurs, this algorithm proceeds in some probe sequence until an unoccupied slot is found. This startegy consists of storing all records in a single array. For example, a simple implementation of this strategy would be to proceed one element further every time that the calculated index is already occupied, until an unoccupied spot is found. This implementation is called linear probing.

    Xem xét ví dụ sau đây về tên ánh xạ bảng băm đơn giản đến số điện thoại bằng hàm băm của bạn từ trước đó:

Hướng dẫn how dictionary works under the hood in python - cách từ điển hoạt động dưới mui xe trong python

John và Lisa va chạm kể từ khi cả hai băm với chỉ số

>>> hash("hash")
832529968546820528

>>> # different run
>>> hash("hash")
8049792375956551724
1. Lisa đã được chèn vào sau khi John, vì vậy nó đã được đưa vào một chỉ số hơn nữa -
>>> hash("hash")
832529968546820528

>>> # different run
>>> hash("hash")
8049792375956551724
2. Lưu ý rằng Sandra Dee có một hàm băm độc đáo, nhưng tuy nhiên đã va chạm với Lisa Smith, trước đây đã va chạm với John Smith.

  • Chuỗi riêng biệt: Trái ngược với địa chỉ mở, chiến lược này bao gồm lưu trữ nhiều mảng. Mỗi bản ghi chứa một mảng riêng biệt chứa tất cả các phần tử có cùng chỉ mục được tính toán. Sau đây là bảng mẫu từ trước đó, sử dụng chiến lược chuỗi riêng biệt: As opposed to open addressing, this strategy consists of storing multiple arrays. Each record contains a separate array which holds all of the elements with the same calculated index. The following is the sample table from earlier, using the separate chaining strategy:
Hướng dẫn how dictionary works under the hood in python - cách từ điển hoạt động dưới mui xe trong python

Lần này, Sandra Dee đã không va chạm với Sandra vì mỗi phần tử giữ một con trỏ đến một loạt các bản ghi va chạm.

Hiểu bảng băm Python

Khi bạn nghĩ về nó, các khóa từ điển Python cho phép nhiều loại hơn là chỉ số nguyên. Chúng có thể là chuỗi, chức năng, và nhiều hơn nữa. Làm thế nào để Python lưu trữ các phím này bên trong bộ nhớ và biết nơi để tìm giá trị của chúng?

Bạn đoán đúng - bàn băm! Python thực hiện các bảng băm dưới mui xe, một triển khai hoàn toàn không thể đối với bạn với tư cách là một nhà phát triển. Tuy nhiên, nó có thể được sử dụng tuyệt vời để bạn hiểu cách thực hiện các bảng băm python, tối ưu hóa của chúng và cách sử dụng chúng một cách khôn ngoan.

Hãy cùng đi sâu vào một số bên trong để hiểu rõ hơn về cách tất cả các khái niệm mới mà bạn vừa học được được đưa vào thực hành.

Khám phá chức năng băm python

Chức năng băm Python lấy một đối tượng băm và băm thành 32/64 bit (phụ thuộc vào kiến ​​trúc hệ thống). Các bit được phân phối tốt như có thể thấy trong ví dụ sau, cho thấy hai chuỗi rất giống nhau - chuỗi liên tiếp, liên tiếp, thường được sử dụng trong từ điển, với các giá trị băm rất khác nhau:


>>> "{0:b}".format(hash('hash2'))
>>> '110000010001001001011000101001010110101110010010000001101111'

>>> "{0:b}".format(hash('hash'))
>>> '101110001101101111011001100001110000000110011011000110110000'

Phân phối này làm giảm tỷ lệ va chạm băm, từ đó làm cho từ điển nhanh hơn nhiều. Hơn nữa, bạn nên biết rằng giá trị băm chỉ không đổi cho trường hợp hiện tại của quy trình. Bạn có thể đã vấp ngã khi băm khác nhau của cùng một đối tượng và tự hỏi tại sao nó lại xảy ra. Lý do chính cho hiện tượng này có liên quan đến bảo mật: các bảng băm dễ bị ảnh hưởng bởi các cuộc tấn công dos va chạm khi sử dụng các giá trị băm không đổi. Python 3.6 đã giới thiệu việc thực hiện Siphash để ngăn chặn các cuộc tấn công này. Bạn có thể đọc thêm về nó trên PEP 456. Sau đây thể hiện các băm khác nhau trên hai lần chạy khác nhau:

>>> hash("hash")
832529968546820528

>>> # different run
>>> hash("hash")
8049792375956551724

Xử lý va chạm trong Python

Python sử dụng địa chỉ mở để giải quyết các trường hợp băm. Mã nguồn Python Suggets rằng địa chỉ mở được ưu tiên hơn chuỗi vì chi phí liên kết để chuỗi sẽ là đáng kể. Trước đó, bạn đã thấy một ví dụ về thăm dò tuyến tính. Python sử dụng thăm dò ngẫu nhiên, như có thể thấy trong mã nguồn hoặc trong mã Python rất đơn giản này:open addressing to resolve hash coliisions. Python source code suggets that open addressing is preferred over chaining since the link overhead for chaining would be substantial. Earlier, you’ve seen an example of linear probing. Python uses random probing, as can be seen in the source code, or in this very simplified Python code:

perturb = hash
perturb >>= 5
new_hash = (current_index*5 + 1 + perturb)

Giống như thăm dò tuyến tính, phần đầu tiên của thuật toán này tiến hành một cách cố định (

>>> hash("hash")
832529968546820528

>>> # different run
>>> hash("hash")
8049792375956551724
3). Các nhà phát triển Python Core đã tìm thấy nó là tốt trong những trường hợp phổ biến trong đó các khóa băm liên tiếp. Phần khác của thuật toán này là những gì làm cho nó trở nên ngẫu nhiên - biến
>>> hash("hash")
832529968546820528

>>> # different run
>>> hash("hash")
8049792375956551724
4 phụ thuộc vào các bit thực tế của băm, và như bạn đã thấy trước đây - chúng được phân phối tốt và thường không giống nhau ngay cả đối với các khóa tương tự.

Xóa các phần tử bằng các giá trị giả

Trong một thế giới hoàn hảo không có va chạm, việc xóa các phần tử sẽ đơn giản: chỉ cần xóa phần tử ở chỉ mục mong muốn để nó có thể được nạp lại với các giá trị khác. Thật không may, chúng ta không sống trong một thế giới hoàn hảo, và các vụ va chạm xảy ra thường xuyên. Bạn có nhớ ví dụ địa chỉ mở từ trước không?

Hướng dẫn how dictionary works under the hood in python - cách từ điển hoạt động dưới mui xe trong python

Bây giờ giả sử bạn muốn xóa John Smith khỏi bàn. Có vẻ tầm thường, phải không? Hash John Smith và xóa phần tử nằm ở chỉ số tính toán. Bây giờ, bạn có thể đã nhận thấy vấn đề trong phương pháp này. Sau khi xóa John, Sandra là không thể truy cập! Băm Sandra sẽ đưa chúng ta đến một khe trống. Vì lý do chính xác này, Python thực hiện các giá trị giả - thay vì xóa hoàn toàn phần tử John, nó sẽ đặt một giá trị giả cố định ở đó. Khi thuật toán phải đối mặt với một giá trị giả, nó biết rằng có một giá trị ở đó nhưng nó đã bị xóa. Sau đó, nó tiếp tục thăm dò về phía trước.

Thực hiện mọi thứ với Python

Bây giờ bạn đã biết các bảng băm là gì, chức năng băm python hoạt động như thế nào và Python xử lý va chạm như thế nào, đã đến lúc thấy những điều này hoạt động bằng cách khám phá việc thực hiện từ điển và phương pháp tra cứu. Phương pháp tra cứu được sử dụng trong tất cả các hoạt động: tìm kiếm, chèn và xóa.

Điều đầu tiên bạn cần biết là Python khởi tạo một dict với một mảng 8 phần tử. Các thí nghiệm cho thấy kích thước này đủ cho hầu hết các dicts phổ biến, thường được tạo để vượt qua các đối số từ khóa. Mỗi phần tử trong mảng chứa một cấu trúc chứa khóa, giá trị và băm. Hash được lưu trữ để không tính toán lại với mỗi lần tăng kích thước của từ điển (được giải thích thêm trong việc khám phá các bảng Hash Hash tối ưu hóa: Thay đổi kích thước từ điển).

Ghi chú

Cho đến bây giờ, các chỉ số bộ nhớ được hiển thị dưới dạng các giá trị thập phân. Từ thời điểm này trở đi, bạn sẽ nhận thấy rằng các địa chỉ bộ nhớ sẽ được hiển thị dưới dạng số nguyên nhị phân. Don Tiết được sợ hãi! Bạn có thể tiếp tục đọc ngay cả khi bạn không quen thuộc với họ - Tôi chỉ sử dụng nó để bạn hiểu rõ hơn về cách các bảng Hash Python hoạt động với các bit, nhưng nó không bắt buộc phải hiểu điều đó.

Trên phương pháp tra cứu. Ở đây, một phiên bản Python đơn giản hóa, tiếp theo là một lời giải thích:

 1    DUMMY = -2
 2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
 3        mask = len(table) - 1
 4        freeslot = None
 5        for index in generate_probes(hash_, mask):
 6            elem = table[index]
 7            if elem is None:
 8                return (index, None) if freeslot is None else (freeslot, DUMMY)
 9            elif elem == DUMMY:
10                if freeslot is None:
11                    freeslot = index
12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
13                return (index, elem)

  • Dòng 5 tính toán chỉ mục bằng phương thức

    >>> hash("hash")
    832529968546820528
    
    >>> # different run
    >>> hash("hash")
    8049792375956551724
    
    5 được hiển thị trong khối mã tiếp theo bên dưới. calculates the index with the
    >>> hash("hash")
    832529968546820528
    
    >>> # different run
    >>> hash("hash")
    8049792375956551724
    
    5 method shown in the next code block below.

  • Dòng 8 kiểm tra xem chỉ mục tìm thấy có trống không và trả về một bộ

    >>> hash("hash")
    832529968546820528
    
    >>> # different run
    >>> hash("hash")
    8049792375956551724
    
    6 để người gọi xử lý (hoạt động tìm kiếm sẽ tăng một keyerror, chèn sẽ chèn), trừ khi một hình nộm được tìm thấy trước đó, trong trường hợp đó trả về
    >>> hash("hash")
    832529968546820528
    
    >>> # different run
    >>> hash("hash")
    8049792375956551724
    
    7 để người gọi xử lý (Hoạt động tìm kiếm sẽ tăng KeyError nhưng nó phải tiếp tục tìm kiếm sau hình nộm).
    checks if the found index is empty, and returns a tuple
    >>> hash("hash")
    832529968546820528
    
    >>> # different run
    >>> hash("hash")
    8049792375956551724
    
    6 for the caller to handle (search operation would raise a KeyError, insertion would insert), unless a dummy was found earlier, in which case return
    >>> hash("hash")
    832529968546820528
    
    >>> # different run
    >>> hash("hash")
    8049792375956551724
    
    7 for the caller to handle (search operation would raise KeyError but it had to continue searching after the dummy).

  • Dòng 9 kiểm tra xem chỉ mục tìm thấy có chứa giá trị giả hay không, nếu vậy nó tiếp tục tìm kiếm và lưu chỉ mục đó cho dòng 8. checks if the found index contains a dummy value, if so it keeps searching and saving that index for line 8.

  • Dòng 12 so sánh danh tính của các khóa. Nếu chúng là cùng một đối tượng, chỉ số được trả về. compares the identities of the keys. If they are the same object, the index is returned.

    Nếu không, nó so sánh giá trị khóa và băm. Như đã biết, các đối tượng bằng nhau nên có băm bằng nhau, điều đó có nghĩa là các đối tượng có băm khác nhau không bằng nhau. Nếu cả hai đều bằng nhau, chỉ số được trả về.and the hash. As it is known, equal objects should have equal hashes, which means that objects with different hashes are not equal. If both are equal, the index is returned.

  • Nếu phần tử mong muốn chưa được tìm thấy (hãy nhớ rằng khe không trống/giả), thì đây là tình huống va chạm băm - trong đó tập lệnh quay lại phương thức

    >>> hash("hash")
    832529968546820528
    
    >>> # different run
    >>> hash("hash")
    8049792375956551724
    
    5 để tính toán băm mới ngẫu nhiên và sau đó quay lại đến bước đầu tiên.

1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
2        index = hash_ & mask
3        yield index
4        perturb = hash_
5        while True:
6            new_hash = index * 5 + 1 + perturb
7            yield new_hash & mask
8            perturb >>= 5

  • Mặt nạ dòng 3 Các bit băm với kích thước của bảng trừ một - ví dụ, trong một bảng có kích thước 8, 3 bit cuối cùng sẽ được thực hiện (111 trong nhị phân bằng 7 trong thập phân, do đó 3 bit có thể biểu thị 0- 7). Trình diễn sau đây cho thấy một ví dụ băm với 3 bit cuối cùng được thực hiện để lập chỉ mục: masks the hash bits with the size of the table minus one - For example, in a table with the size of 8, the last 3 bits would be taken (111 in binary equals 7 in decimal, so 3 bits can represent 0-7). The following demonstration shows a hash example with its last 3 bits taken for indexing:
Hướng dẫn how dictionary works under the hood in python - cách từ điển hoạt động dưới mui xe trong python
  • Dòng 7: Hãy nhớ rằng thuật toán quay trở lại phương thức
    >>> hash("hash")
    832529968546820528
    
    >>> # different run
    >>> hash("hash")
    8049792375956551724
    
    5 nếu mong muốn được tìm thấy? Đây là dòng mà nó quay trở lại. Nó tính toán băm mới bằng cách sử dụng đầu dò ngẫu nhiên và mang lại điều khiển trở lại phương thức
    perturb = hash
    perturb >>= 5
    new_hash = (current_index*5 + 1 + perturb)
    
    0.
    : Remember that the algorithm goes back to the
    >>> hash("hash")
    832529968546820528
    
    >>> # different run
    >>> hash("hash")
    8049792375956551724
    
    5 method if the desired wasn’t found? This is the line that it goes back to. It computes the new hash using a random probe and yields control back to the
    perturb = hash
    perturb >>= 5
    new_hash = (current_index*5 + 1 + perturb)
    
    0 method.

Nếu bạn đã viết hoạt động tìm kiếm, nó sẽ xem xét các dòng sau:

1    def search(key: Any) -> Any:  # usually implemented as __getitem__
2        hashvalue = hash(key)
3        index, elem = lookup(key, hashvalue)
4        if elem is None or elem == DUMMY:
5            raise KeyError(key)
6        return table[index]

Như tôi đã đề cập trước đây, việc xử lý

perturb = hash
perturb >>= 5
new_hash = (current_index*5 + 1 + perturb)
1 và
perturb = hash
perturb >>= 5
new_hash = (current_index*5 + 1 + perturb)
2 được thực hiện trong người gọi - trong khi phương pháp cụ thể này tăng
perturb = hash
perturb >>= 5
new_hash = (current_index*5 + 1 + perturb)
3, các hoạt động khác vẫn có thể sử dụng chỉ số đó như bạn sẽ sớm thấy.

Kiểm tra hiểu

Hoạt động chèn sẽ sử dụng chỉ số nhận được từ phương thức tra cứu ngay cả khi phần tử là

perturb = hash
perturb >>= 5
new_hash = (current_index*5 + 1 + perturb)
2 hoặc
perturb = hash
perturb >>= 5
new_hash = (current_index*5 + 1 + perturb)
1?

Hiển thị giải pháp

Đúng! Phương pháp chèn sẽ rất vui khi nhận được một khe trống - điều đó có nghĩa là không có va chạm băm nào liên quan đến quá trình này.

Hiểu bộ Python

Cùng với từ điển, các bảng băm Python cũng đóng vai trò là cấu trúc cơ bản cho các bộ. Cả hai triển khai đều khá giống nhau như có thể thấy trong mã nguồn, ngoại trừ việc đặt không lưu trữ giá trị cho từng khóa, có nghĩa là tối ưu hóa của Python, được hiển thị sau trong bài viết này, không áp dụng cho chúng. Việc sử dụng các bảng băm cho các bộ làm cho hoạt động tra cứu, được sử dụng thường xuyên trong các bộ để giữ chúng mà không cần sao chép, khá nhanh (như bạn nên biết bây giờ - nó luôn phụ thuộc vào sự va chạm).

Khám phá Tối ưu hóa bảng Hash Python

Các phương pháp trên không hoàn toàn giống với Python. Tôi đã không muốn quá tạo thành mọi thứ, vì vậy tôi đã bỏ qua một số chi tiết làm cho từ điển Python rực rỡ nhanh chóng. Phần sau đây sẽ hướng dẫn bạn xây dựng một từ điển tùy chỉnh, thực hiện các tối ưu hóa của các bảng băm Python.

Bước đầu tiên của bạn là tạo một lớp

perturb = hash
perturb >>= 5
new_hash = (current_index*5 + 1 + perturb)
6:

 1    class Dictionary:
 2
 3        def __init__(self, *args, **kwargs):
 4            """Dict initializiation"""
 5
 6        @staticmethod
 7        def _generate_probes(hash_: int, mask: int) -> Iterable[int]:
 8            index = hash_ & mask
 9            yield index
10            perturb = hash_
11            while True:
12                new_hash = index * 5 + 1 + perturb
13                yield new_hash & mask
14                perturb >>= 5
15
16        def _lookup(self, key: Any, hashvalue: int) -> Tuple[int, Any]:
17            """The lookup method"""
18
19        def __getitem__(self, key: Any) -> Any:
20            """Get value from dict"""
21
22        def __setitem__(self, key: Any, value: Any):
23            """Insert item to dict"""
24
25        def __delitem__(self, key: Any):
26            """Delete item from dict"""
27
28        def __len__(self) -> int:
29            """Dict length"""
30
31        def __iter__(self) -> Iterable:
32            """Iterate through dictionary"""
33
34        def __contains__(self, key: Any) -> bool:
35            """Check if dictionary contains a key"""
36
37        def __repr__(self) -> str:
38            """Dict representation"""

Bạn sẽ sớm điền vào các phương pháp này. Lưu ý rằng

>>> hash("hash")
832529968546820528

>>> # different run
>>> hash("hash")
8049792375956551724
5 hiện là một phương thức tĩnh - không có lý do gì để nó là một phương thức thể hiện vì nó không sử dụng bất kỳ thuộc tính thể hiện nào.
perturb = hash
perturb >>= 5
new_hash = (current_index*5 + 1 + perturb)
0 và
perturb = hash
perturb >>= 5
new_hash = (current_index*5 + 1 + perturb)
9 từ trước không được sử dụng vì cả hai sẽ thay đổi khá nhiều.

Hãy để Lặn đi vào các tối ưu hóa bàn băm Python!

Từ điển nhỏ gọn

Từ điển nhỏ gọn tối ưu hóa không gian có băm các bảng chiếm. Trước khi chúng được thực hiện, Python đã có các bảng băm thưa thớt - mỗi khe không có người ở lại chiếm nhiều không gian như một khe chiếm vì nó phải tiết kiệm không gian cho chìa khóa, băm và giá trị. Từ điển nhỏ gọn đã giới thiệu một bảng nhỏ hơn nhiều chỉ cho các chỉ số và một bảng riêng cho các khóa, giá trị và băm. Bằng cách này, bảng chỉ số có thể là cái thưa thớt trong khi bảng lớn hơn dày đặc.

Ví dụ, trước các từ điển nhỏ gọn, sau đây là cách từ điển và mảng bộ nhớ tương ứng của nó trông giống như (được lấy từ văn bản Raymond Hettinger,):

d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}

[['---', '---', '---'],
    [-8522787127447073495, 'barry', 'green'],
    ['---', '---', '---'],
    ['---', '---', '---'],
    ['---', '---', '---'],
    [-9092791511155847987, 'timmy', 'red'],
    ['---', '---', '---'],
    [-6480567542315338377, 'guido', 'blue']]

Thay vào đó, với từ điển nhỏ gọn, hai bảng khác nhau được xây dựng:

length of the key % table_size (3)
0

Lấy Timmy làm ví dụ. Nó được băm vào chỉ số thứ 5, trong đó chứa số

>>> hash("hash")
832529968546820528

>>> # different run
>>> hash("hash")
8049792375956551724
1 trong bảng chỉ số, từ đó chứa phần tử Timmy thực tế trong bảng mục nhập.

Raymond Hettinger, người tạo ra từ điển nhỏ gọn, cho biết:

Tiết kiệm bộ nhớ là đáng kể (từ nén từ 30% đến 95% tùy thuộc vào mức độ đầy đủ của bảng).

Ngoài tiết kiệm không gian, bố cục bộ nhớ mới giúp lặp lại nhanh hơn. Các phím/giá trị/mục có thể lặp trực tiếp trên bảng dày đặc, sử dụng ít truy cập bộ nhớ hơn.

Phần mã này chèn các phần tử mới vào từ điển và kiểm tra kích thước sau mỗi lần chèn cho đến khi nó đạt được 1000 phần tử. Tại đây, cách thức kích thước từ điển nhỏ gọn Python 3.8 so với Python 2.7 Từ điển không nhỏ gọn: Từ điển:

Số chìa khóa3,8 Kích thước2.7 Kích thướcSố nhân
< 6 232 280 1.2
< 11 360 1048 2.9
< 22 640 1048 1.6
< 43 1176 3352 2.8
< 86 2272 3352 1.5
< 171 4696 12568 2.5
< 342 9312 12568 1.3
< 683 18520 49432 2.7
< 1000 36960 49432 1.3

Để thực hiện tính năng này trong từ điển tùy chỉnh của bạn, từ điển cần triển khai hai mảng tách biệt: một cho các chỉ số và một cho các mục thực tế. Trước khi bạn làm điều đó, có những tối ưu hóa khác để xem xét, do đó, việc triển khai cuối cùng sẽ diễn ra sớm, trong việc kết hợp tất cả lại với nhau.

Trong khi đó, bạn có thể tạo phương thức xây dựng mảng chỉ số và khởi tạo lớp với các biến

 1    DUMMY = -2
 2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
 3        mask = len(table) - 1
 4        freeslot = None
 5        for index in generate_probes(hash_, mask):
 6            elem = table[index]
 7            if elem is None:
 8                return (index, None) if freeslot is None else (freeslot, DUMMY)
 9            elif elem == DUMMY:
10                if freeslot is None:
11                    freeslot = index
12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
13                return (index, elem)
1,
 1    DUMMY = -2
 2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
 3        mask = len(table) - 1
 4        freeslot = None
 5        for index in generate_probes(hash_, mask):
 6            elem = table[index]
 7            if elem is None:
 8                return (index, None) if freeslot is None else (freeslot, DUMMY)
 9            elif elem == DUMMY:
10                if freeslot is None:
11                    freeslot = index
12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
13                return (index, elem)
2 và
 1    DUMMY = -2
 2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
 3        mask = len(table) - 1
 4        freeslot = None
 5        for index in generate_probes(hash_, mask):
 6            elem = table[index]
 7            if elem is None:
 8                return (index, None) if freeslot is None else (freeslot, DUMMY)
 9            elif elem == DUMMY:
10                if freeslot is None:
11                    freeslot = index
12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
13                return (index, elem)
3:

length of the key % table_size (3)
1

  • Mảng chỉ số thuộc một loại duy nhất, do đó

    perturb = hash
    perturb >>= 5
    new_hash = (current_index*5 + 1 + perturb)
    
    1 và
     1    DUMMY = -2
     2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
     3        mask = len(table) - 1
     4        freeslot = None
     5        for index in generate_probes(hash_, mask):
     6            elem = table[index]
     7            if elem is None:
     8                return (index, None) if freeslot is None else (freeslot, DUMMY)
     9            elif elem == DUMMY:
    10                if freeslot is None:
    11                    freeslot = index
    12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
    13                return (index, elem)
    
    5 phải cùng loại.

  • Mảng

     1    DUMMY = -2
     2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
     3        mask = len(table) - 1
     4        freeslot = None
     5        for index in generate_probes(hash_, mask):
     6            elem = table[index]
     7            if elem is None:
     8                return (index, None) if freeslot is None else (freeslot, DUMMY)
     9            elif elem == DUMMY:
    10                if freeslot is None:
    11                    freeslot = index
    12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
    13                return (index, elem)
    
    1 sẽ giữ bảng chỉ số thưa thớt (chỉ vào các mục thực tế)

  •  1    DUMMY = -2
     2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
     3        mask = len(table) - 1
     4        freeslot = None
     5        for index in generate_probes(hash_, mask):
     6            elem = table[index]
     7            if elem is None:
     8                return (index, None) if freeslot is None else (freeslot, DUMMY)
     9            elif elem == DUMMY:
    10                if freeslot is None:
    11                    freeslot = index
    12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
    13                return (index, elem)
    
    3 và
     1    DUMMY = -2
     2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
     3        mask = len(table) - 1
     4        freeslot = None
     5        for index in generate_probes(hash_, mask):
     6            elem = table[index]
     7            if elem is None:
     8                return (index, None) if freeslot is None else (freeslot, DUMMY)
     9            elif elem == DUMMY:
    10                if freeslot is None:
    11                    freeslot = index
    12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
    13                return (index, elem)
    
    2 Các biến được sử dụng để giữ kích thước của từ điển có và không có các giá trị giả (tương ứng)

  •  1    DUMMY = -2
     2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
     3        mask = len(table) - 1
     4        freeslot = None
     5        for index in generate_probes(hash_, mask):
     6            elem = table[index]
     7            if elem is None:
     8                return (index, None) if freeslot is None else (freeslot, DUMMY)
     9            elif elem == DUMMY:
    10                if freeslot is None:
    11                    freeslot = index
    12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
    13                return (index, elem)
    
    9 sử dụng mô -đun mảng để biểu diễn một cách nhỏ gọn một mảng các giá trị cơ bản. Nó tuân theo logic của mã nguồn Python để xác định kích thước của các chỉ số.

Ghi chú

Từ điển nhỏ gọn đã được thực hiện trong Python 3.6.

Từ điển chia sẻ chính

Python 3.3 giới thiệu từ điển chia sẻ khóa. Khi từ điển được tạo ra để lấp đầy khe mô tả của một đối tượng, chúng được tạo ở dạng phân chia. Bảng khóa được lưu trong bộ nhớ cache trong loại, có khả năng cho phép tất cả các từ điển thuộc tính của các trường hợp của một lớp chia sẻ các khóa. Hành vi này xảy ra trong phương pháp

1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
2        index = hash_ & mask
3        yield index
4        perturb = hash_
5        while True:
6            new_hash = index * 5 + 1 + perturb
7            yield new_hash & mask
8            perturb >>= 5
0 của các lớp và nhằm mục đích tiết kiệm không gian bộ nhớ và cải thiện tốc độ tạo đối tượng. Takeaway của bạn từ điều này nên luôn luôn cố gắng gán các thuộc tính trong phương thức
1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
2        index = hash_ & mask
3        yield index
4        perturb = hash_
5        while True:
6            new_hash = index * 5 + 1 + perturb
7            yield new_hash & mask
8            perturb >>= 5
1 để các lớp tùy chỉnh của bạn có thể sử dụng từ điển chia sẻ khóa.dict slot of an object, they are created in split form. The keys table is cached in the type, potentially allowing all attribute dictionaries of instances of one class to share keys. This behaviour happens in the
1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
2        index = hash_ & mask
3        yield index
4        perturb = hash_
5        while True:
6            new_hash = index * 5 + 1 + perturb
7            yield new_hash & mask
8            perturb >>= 5
0 method of classes, and aims to save memory space and to improve speed of object creation. Your takeaway from this should be to always strive to assign attributes in the
1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
2        index = hash_ & mask
3        yield index
4        perturb = hash_
5        while True:
6            new_hash = index * 5 + 1 + perturb
7            yield new_hash & mask
8            perturb >>= 5
1 method so your custom classes can use key-sharing dictionaries.

Visual sau đây cho thấy ba trường hợp của cùng một lớp:

Hướng dẫn how dictionary works under the hood in python - cách từ điển hoạt động dưới mui xe trong python

Bạn có thể thấy rằng mỗi trường hợp chỉ giữ các giá trị của nó, trong khi có một vị trí được chia sẻ duy nhất trong bộ nhớ cho các khóa.

Hãy để thực hiện tính năng tuyệt vời này trong từ điển của bạn:

length of the key % table_size (3)
2

  • Dòng 1 tạo một lớp

    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    2 giữ một khóa và giá trị băm của nó creates a
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    2 class which holds a key and its hash value

  • Các dòng 15-16 Đặt hai danh sách riêng cho các mục: một cho các khóa, có thể được chia sẻ với các trường hợp khác và một cho các giá trị. sets two separate lists for entries: One for the keys, which may be shared with other instances, and one for the values.

  • Dòng 19: Từ điển được lên kế hoạch khởi tạo với các giá trị. Phương pháp

    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    3 đảm nhận điều đó: bất kể loại đối số nào nhận được, miễn là nó có thể đi được, từ điển sẽ sử dụng nội dung của nó.: The dictionary is planned to be initialized with values. The
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    3 method takes care of that: No matter what type of argument received, as long as its an iterable, the dictionary will use its contents.

  • Dòng 22 đặt

    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    4 thành
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    5 để đánh dấu xem trường hợp này có chia sẻ các khóa của nó với các trường hợp khác hay không.
    sets
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    4 to
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    5 in order to mark whether this instance shares its keys with other instances.

  • Các dòng 23 kiểm tra xem một đối số

    perturb = hash
    perturb >>= 5
    new_hash = (current_index*5 + 1 + perturb)
    
    6 được đưa ra làm đối số, thì nó tiếp tục kiểm tra xem từ điển hiện tại đã được lấp đầy với các khóa và giá trị. Nếu vậy, nó sao chép các khóa và giá trị từ từ điển khác. checks if a
    perturb = hash
    perturb >>= 5
    new_hash = (current_index*5 + 1 + perturb)
    
    6 arguments was given as argument, then it goes on to checking if the current dictionary is already filled with keys and values. If so, it copies the keys and values from the other dictionary.

    Nếu từ điển hiện tại không được lấp đầy, thì có một sự chia sẻ quan trọng! Dòng 28 sao chép từ điển khác

     1    DUMMY = -2
     2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
     3        mask = len(table) - 1
     4        freeslot = None
     5        for index in generate_probes(hash_, mask):
     6            elem = table[index]
     7            if elem is None:
     8                return (index, None) if freeslot is None else (freeslot, DUMMY)
     9            elif elem == DUMMY:
    10                if freeslot is None:
    11                    freeslot = index
    12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
    13                return (index, elem)
    
    1 để bảo tồn chức năng.
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    8 và
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    9 phải vẫn đồng bộ, do đó
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    9 cần phải được đưa ra với các giá trị
    perturb = hash
    perturb >>= 5
    new_hash = (current_index*5 + 1 + perturb)
    
    2 vì bạn không muốn các giá trị của từ điển khác. Trường hợp khác
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    8 sau đó chỉ được tham chiếu bởi
    1    def search(key: Any) -> Any:  # usually implemented as __getitem__
    2        hashvalue = hash(key)
    3        index, elem = lookup(key, hashvalue)
    4        if elem is None or elem == DUMMY:
    5            raise KeyError(key)
    6        return table[index]
    
    3.Line 28 copies the other dictionary
     1    DUMMY = -2
     2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
     3        mask = len(table) - 1
     4        freeslot = None
     5        for index in generate_probes(hash_, mask):
     6            elem = table[index]
     7            if elem is None:
     8                return (index, None) if freeslot is None else (freeslot, DUMMY)
     9            elif elem == DUMMY:
    10                if freeslot is None:
    11                    freeslot = index
    12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
    13                return (index, elem)
    
    1 in order to preserve functionallity.
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    8 and
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    9 must remain in sync, so
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    9 needs to be initalized with
    perturb = hash
    perturb >>= 5
    new_hash = (current_index*5 + 1 + perturb)
    
    2 values since you don’t want the values of the other dictionary. The other instance
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    8 is then only referenced by
    1    def search(key: Any) -> Any:  # usually implemented as __getitem__
    2        hashvalue = hash(key)
    3        index, elem = lookup(key, hashvalue)
    4        if elem is None or elem == DUMMY:
    5            raise KeyError(key)
    6        return table[index]
    
    3.

  • Nếu trường hợp khác không thuộc loại

    perturb = hash
    perturb >>= 5
    new_hash = (current_index*5 + 1 + perturb)
    
    6, dòng 34 kiểm tra xem nó có thuộc tính
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    8 hay không, giống như một từ điển thực. Nếu vậy, nó sao chép các khóa và giá trị của nó.line 34 checks if it has a
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    8 attribute, like a real dictionary. If so, it copies its keys and values.

  • Nếu tất cả những người khác thất bại, dòng 38 đối xử với thể hiện khác như thể nó có khóa và giá trị trong đó và sao chép chúng.line 38 treats the other instance as if it had keys and values in it and copy them.

  • Cuối cùng, dòng 40 sao chép các khóa và giá trị được đưa ra trong

    1    def search(key: Any) -> Any:  # usually implemented as __getitem__
    2        hashvalue = hash(key)
    3        index, elem = lookup(key, hashvalue)
    4        if elem is None or elem == DUMMY:
    5            raise KeyError(key)
    6        return table[index]
    
    6.line 40 copies the keys and values given in
    1    def search(key: Any) -> Any:  # usually implemented as __getitem__
    2        hashvalue = hash(key)
    3        index, elem = lookup(key, hashvalue)
    4        if elem is None or elem == DUMMY:
    5            raise KeyError(key)
    6        return table[index]
    
    6 as well.

  • 1    def search(key: Any) -> Any:  # usually implemented as __getitem__
    2        hashvalue = hash(key)
    3        index, elem = lookup(key, hashvalue)
    4        if elem is None or elem == DUMMY:
    5            raise KeyError(key)
    6        return table[index]
    
    7 Chuyển đổi từ điển thành từ điển không chia sẻ. Nó được lên kế hoạch để được gọi khi một từ điển chung cố gắng thay đổi giá trị của nó.

Từ điển thay đổi kích thước

Python kiểm tra kích thước bảng mỗi khi chúng tôi thêm một phím và nếu bảng là hai phần ba, nó sẽ thay đổi kích thước bảng băm. Nếu một từ điển có 50000 khóa hoặc ít hơn, kích thước mới là

1    def search(key: Any) -> Any:  # usually implemented as __getitem__
2        hashvalue = hash(key)
3        index, elem = lookup(key, hashvalue)
4        if elem is None or elem == DUMMY:
5            raise KeyError(key)
6        return table[index]
8, nếu không, đó là
1    def search(key: Any) -> Any:  # usually implemented as __getitem__
2        hashvalue = hash(key)
3        index, elem = lookup(key, hashvalue)
4        if elem is None or elem == DUMMY:
5            raise KeyError(key)
6        return table[index]
9. Hãy nhớ rằng Python lưu trữ giá trị băm cùng với khóa và giá trị? Đây là nơi nó có ích! Thay vì thử lại các phím khi chèn chúng vào bảng lớn hơn, băm được lưu trữ được sử dụng. Bạn có thể tự hỏi - điều gì sẽ xảy ra nếu đối tượng chính được thay đổi? Trong trường hợp này, băm nên được tính toán lại và giá trị được lưu trữ sẽ không chính xác? Một tình huống như vậy là không thể, vì các loại có thể thay đổi không thể là chìa khóa của một từ điển. Tại đây, cách bạn thực hiện hoạt động thay đổi kích thước:

length of the key % table_size (3)
3

  • Dòng 3: Kích thước bảng Hash Python là sức mạnh của 2, vì vậy chúng tôi cũng sẽ sử dụng quyền hạn 2. Lý do chính Python sử dụng các công suất vòng tròn của 2 là hiệu quả: tính toán

     1    class Dictionary:
     2
     3        def __init__(self, *args, **kwargs):
     4            """Dict initializiation"""
     5
     6        @staticmethod
     7        def _generate_probes(hash_: int, mask: int) -> Iterable[int]:
     8            index = hash_ & mask
     9            yield index
    10            perturb = hash_
    11            while True:
    12                new_hash = index * 5 + 1 + perturb
    13                yield new_hash & mask
    14                perturb >>= 5
    15
    16        def _lookup(self, key: Any, hashvalue: int) -> Tuple[int, Any]:
    17            """The lookup method"""
    18
    19        def __getitem__(self, key: Any) -> Any:
    20            """Get value from dict"""
    21
    22        def __setitem__(self, key: Any, value: Any):
    23            """Insert item to dict"""
    24
    25        def __delitem__(self, key: Any):
    26            """Delete item from dict"""
    27
    28        def __len__(self) -> int:
    29            """Dict length"""
    30
    31        def __iter__(self) -> Iterable:
    32            """Iterate through dictionary"""
    33
    34        def __contains__(self, key: Any) -> bool:
    35            """Check if dictionary contains a key"""
    36
    37        def __repr__(self) -> str:
    38            """Dict representation"""
    
    0 có thể được triển khai bằng cách sử dụng các hoạt động bit, như bạn đã thấy trước đây .: Python hash table sizes are powers of 2, so we will also use powers of 2. The primary reason Python uses “round” powers of 2 is efficiency: computing
     1    class Dictionary:
     2
     3        def __init__(self, *args, **kwargs):
     4            """Dict initializiation"""
     5
     6        @staticmethod
     7        def _generate_probes(hash_: int, mask: int) -> Iterable[int]:
     8            index = hash_ & mask
     9            yield index
    10            perturb = hash_
    11            while True:
    12                new_hash = index * 5 + 1 + perturb
    13                yield new_hash & mask
    14                perturb >>= 5
    15
    16        def _lookup(self, key: Any, hashvalue: int) -> Tuple[int, Any]:
    17            """The lookup method"""
    18
    19        def __getitem__(self, key: Any) -> Any:
    20            """Get value from dict"""
    21
    22        def __setitem__(self, key: Any, value: Any):
    23            """Insert item to dict"""
    24
    25        def __delitem__(self, key: Any):
    26            """Delete item from dict"""
    27
    28        def __len__(self) -> int:
    29            """Dict length"""
    30
    31        def __iter__(self) -> Iterable:
    32            """Iterate through dictionary"""
    33
    34        def __contains__(self, key: Any) -> bool:
    35            """Check if dictionary contains a key"""
    36
    37        def __repr__(self) -> str:
    38            """Dict representation"""
    
    0 can be implemented using bit operations, as you’ve seen before.

  • Dòng 4 xây dựng một mảng

     1    DUMMY = -2
     2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
     3        mask = len(table) - 1
     4        freeslot = None
     5        for index in generate_probes(hash_, mask):
     6            elem = table[index]
     7            if elem is None:
     8                return (index, None) if freeslot is None else (freeslot, DUMMY)
     9            elif elem == DUMMY:
    10                if freeslot is None:
    11                    freeslot = index
    12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
    13                return (index, elem)
    
    1 mới với kích thước lớn hơn mới. builds a new
     1    DUMMY = -2
     2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
     3        mask = len(table) - 1
     4        freeslot = None
     5        for index in generate_probes(hash_, mask):
     6            elem = table[index]
     7            if elem is None:
     8                return (index, None) if freeslot is None else (freeslot, DUMMY)
     9            elif elem == DUMMY:
    10                if freeslot is None:
    11                    freeslot = index
    12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
    13                return (index, elem)
    
    1 array with the new bigger size.

  • Dòng 5 - 9 vòng thông qua các phím. Đối với mỗi khóa, nó tạo ra một chỉ mục. Nếu chỉ mục đó miễn phí trong bảng chỉ số mới, nó sẽ chèn chỉ mục khóa đó vào bảng chỉ số. Về cơ bản, những gì mã này làm là phân bổ một bảng chỉ số mới và điền nó với các khóa hiện tại. Thông báo không cần băm vì băm được lưu. loops through the keys. For every key, it generates an index. If that index is free in the new indices table, it inserts that key’s index into the indices table. Essentially, what this piece of code does is to allocate a new indices table and fill it up with the current keys. Notice no hashing is needed because the hashes are saved.

  • Dòng 10:

     1    DUMMY = -2
     2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
     3        mask = len(table) - 1
     4        freeslot = None
     5        for index in generate_probes(hash_, mask):
     6            elem = table[index]
     7            if elem is None:
     8                return (index, None) if freeslot is None else (freeslot, DUMMY)
     9            elif elem == DUMMY:
    10                if freeslot is None:
    11                    freeslot = index
    12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
    13                return (index, elem)
    
    2 phải bằng
     1    DUMMY = -2
     2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
     3        mask = len(table) - 1
     4        freeslot = None
     5        for index in generate_probes(hash_, mask):
     6            elem = table[index]
     7            if elem is None:
     8                return (index, None) if freeslot is None else (freeslot, DUMMY)
     9            elif elem == DUMMY:
    10                if freeslot is None:
    11                    freeslot = index
    12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
    13                return (index, elem)
    
    3 vì chưa có gì bị xóa - không nên có bất kỳ giá trị giả nào.
    :
     1    DUMMY = -2
     2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
     3        mask = len(table) - 1
     4        freeslot = None
     5        for index in generate_probes(hash_, mask):
     6            elem = table[index]
     7            if elem is None:
     8                return (index, None) if freeslot is None else (freeslot, DUMMY)
     9            elif elem == DUMMY:
    10                if freeslot is None:
    11                    freeslot = index
    12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
    13                return (index, elem)
    
    2 should be equal to
     1    DUMMY = -2
     2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
     3        mask = len(table) - 1
     4        freeslot = None
     5        for index in generate_probes(hash_, mask):
     6            elem = table[index]
     7            if elem is None:
     8                return (index, None) if freeslot is None else (freeslot, DUMMY)
     9            elif elem == DUMMY:
    10                if freeslot is None:
    11                    freeslot = index
    12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
    13                return (index, elem)
    
    3 since nothing was deleted yet - there should not be any dummy values.

Phiên bản từ điển riêng

Python 3.6 đã thêm một phiên bản riêng tư mới vào từ điển, tăng lên ở mỗi lần tạo từ điển và tại mỗi thay đổi từ điển. Cơ sở lý luận là bỏ qua các tra cứu từ điển nếu phiên bản không thay đổi và thay vào đó sử dụng các giá trị được lưu trong bộ đệm. Việc thực hiện của bạn có thể thực hiện một phiên bản cho mỗi trường hợp, mặc dù nó đã giành được thực hiện bộ nhớ đệm thực tế của các giá trị.

length of the key % table_size (3)
4

  • Biến

     1    class Dictionary:
     2
     3        def __init__(self, *args, **kwargs):
     4            """Dict initializiation"""
     5
     6        @staticmethod
     7        def _generate_probes(hash_: int, mask: int) -> Iterable[int]:
     8            index = hash_ & mask
     9            yield index
    10            perturb = hash_
    11            while True:
    12                new_hash = index * 5 + 1 + perturb
    13                yield new_hash & mask
    14                perturb >>= 5
    15
    16        def _lookup(self, key: Any, hashvalue: int) -> Tuple[int, Any]:
    17            """The lookup method"""
    18
    19        def __getitem__(self, key: Any) -> Any:
    20            """Get value from dict"""
    21
    22        def __setitem__(self, key: Any, value: Any):
    23            """Insert item to dict"""
    24
    25        def __delitem__(self, key: Any):
    26            """Delete item from dict"""
    27
    28        def __len__(self) -> int:
    29            """Dict length"""
    30
    31        def __iter__(self) -> Iterable:
    32            """Iterate through dictionary"""
    33
    34        def __contains__(self, key: Any) -> bool:
    35            """Check if dictionary contains a key"""
    36
    37        def __repr__(self) -> str:
    38            """Dict representation"""
    
    4 giữ một bộ đếm số lượng phiên bản từ điển, vì vậy mỗi trường hợp có thể có một phiên bản duy nhất.

  •  1    class Dictionary:
     2
     3        def __init__(self, *args, **kwargs):
     4            """Dict initializiation"""
     5
     6        @staticmethod
     7        def _generate_probes(hash_: int, mask: int) -> Iterable[int]:
     8            index = hash_ & mask
     9            yield index
    10            perturb = hash_
    11            while True:
    12                new_hash = index * 5 + 1 + perturb
    13                yield new_hash & mask
    14                perturb >>= 5
    15
    16        def _lookup(self, key: Any, hashvalue: int) -> Tuple[int, Any]:
    17            """The lookup method"""
    18
    19        def __getitem__(self, key: Any) -> Any:
    20            """Get value from dict"""
    21
    22        def __setitem__(self, key: Any, value: Any):
    23            """Insert item to dict"""
    24
    25        def __delitem__(self, key: Any):
    26            """Delete item from dict"""
    27
    28        def __len__(self) -> int:
    29            """Dict length"""
    30
    31        def __iter__(self) -> Iterable:
    32            """Iterate through dictionary"""
    33
    34        def __contains__(self, key: Any) -> bool:
    35            """Check if dictionary contains a key"""
    36
    37        def __repr__(self) -> str:
    38            """Dict representation"""
    
    5 được lên kế hoạch được gọi bởi các hoạt động như
     1    class Dictionary:
     2
     3        def __init__(self, *args, **kwargs):
     4            """Dict initializiation"""
     5
     6        @staticmethod
     7        def _generate_probes(hash_: int, mask: int) -> Iterable[int]:
     8            index = hash_ & mask
     9            yield index
    10            perturb = hash_
    11            while True:
    12                new_hash = index * 5 + 1 + perturb
    13                yield new_hash & mask
    14                perturb >>= 5
    15
    16        def _lookup(self, key: Any, hashvalue: int) -> Tuple[int, Any]:
    17            """The lookup method"""
    18
    19        def __getitem__(self, key: Any) -> Any:
    20            """Get value from dict"""
    21
    22        def __setitem__(self, key: Any, value: Any):
    23            """Insert item to dict"""
    24
    25        def __delitem__(self, key: Any):
    26            """Delete item from dict"""
    27
    28        def __len__(self) -> int:
    29            """Dict length"""
    30
    31        def __iter__(self) -> Iterable:
    32            """Iterate through dictionary"""
    33
    34        def __contains__(self, key: Any) -> bool:
    35            """Check if dictionary contains a key"""
    36
    37        def __repr__(self) -> str:
    38            """Dict representation"""
    
    6 và
     1    class Dictionary:
     2
     3        def __init__(self, *args, **kwargs):
     4            """Dict initializiation"""
     5
     6        @staticmethod
     7        def _generate_probes(hash_: int, mask: int) -> Iterable[int]:
     8            index = hash_ & mask
     9            yield index
    10            perturb = hash_
    11            while True:
    12                new_hash = index * 5 + 1 + perturb
    13                yield new_hash & mask
    14                perturb >>= 5
    15
    16        def _lookup(self, key: Any, hashvalue: int) -> Tuple[int, Any]:
    17            """The lookup method"""
    18
    19        def __getitem__(self, key: Any) -> Any:
    20            """Get value from dict"""
    21
    22        def __setitem__(self, key: Any, value: Any):
    23            """Insert item to dict"""
    24
    25        def __delitem__(self, key: Any):
    26            """Delete item from dict"""
    27
    28        def __len__(self) -> int:
    29            """Dict length"""
    30
    31        def __iter__(self) -> Iterable:
    32            """Iterate through dictionary"""
    33
    34        def __contains__(self, key: Any) -> bool:
    35            """Check if dictionary contains a key"""
    36
    37        def __repr__(self) -> str:
    38            """Dict representation"""
    
    7. Nó đặt phiên bản phiên bản hiện tại thành phiên bản mới nhất của biến lớp và tăng biến lớp.

  • Dòng 9 Khởi tạo từ điển với phiên bản mới nhất. initializes the dictionary with the latest version.

Để tất cả chúng cùng nhau

Ghi chú

Mã đầy đủ có thể được tìm thấy ở đây.

Đó là thời gian để sử dụng tất cả các phương pháp mới tuyệt vời này và làm cho từ điển của bạn có thể sử dụng được!

 1    class Dictionary:
 2
 3        def __init__(self, *args, **kwargs):
 4            """Dict initializiation"""
 5
 6        @staticmethod
 7        def _generate_probes(hash_: int, mask: int) -> Iterable[int]:
 8            index = hash_ & mask
 9            yield index
10            perturb = hash_
11            while True:
12                new_hash = index * 5 + 1 + perturb
13                yield new_hash & mask
14                perturb >>= 5
15
16        def _lookup(self, key: Any, hashvalue: int) -> Tuple[int, Any]:
17            """The lookup method"""
18
19        def __getitem__(self, key: Any) -> Any:
20            """Get value from dict"""
21
22        def __setitem__(self, key: Any, value: Any):
23            """Insert item to dict"""
24
25        def __delitem__(self, key: Any):
26            """Delete item from dict"""
27
28        def __len__(self) -> int:
29            """Dict length"""
30
31        def __iter__(self) -> Iterable:
32            """Iterate through dictionary"""
33
34        def __contains__(self, key: Any) -> bool:
35            """Check if dictionary contains a key"""
36
37        def __repr__(self) -> str:
38            """Dict representation"""
8

length of the key % table_size (3)
5

Nó khá giống với phương pháp

perturb = hash
perturb >>= 5
new_hash = (current_index*5 + 1 + perturb)
0 từ trước đó, chỉ lần này nó sử dụng
d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}

[['---', '---', '---'],
    [-8522787127447073495, 'barry', 'green'],
    ['---', '---', '---'],
    ['---', '---', '---'],
    ['---', '---', '---'],
    [-9092791511155847987, 'timmy', 'red'],
    ['---', '---', '---'],
    [-6480567542315338377, 'guido', 'blue']]
0 làm bảng băm,
 1    DUMMY = -2
 2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
 3        mask = len(table) - 1
 4        freeslot = None
 5        for index in generate_probes(hash_, mask):
 6            elem = table[index]
 7            if elem is None:
 8                return (index, None) if freeslot is None else (freeslot, DUMMY)
 9            elif elem == DUMMY:
10                if freeslot is None:
11                    freeslot = index
12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
13                return (index, elem)
5 để kiểm tra các khe trống thay vì
perturb = hash
perturb >>= 5
new_hash = (current_index*5 + 1 + perturb)
2 và
1    def search(key: Any) -> Any:  # usually implemented as __getitem__
2        hashvalue = hash(key)
3        index, elem = lookup(key, hashvalue)
4        if elem is None or elem == DUMMY:
5            raise KeyError(key)
6        return table[index]
3 để kiểm tra sự bình đẳng và nhận dạng của các khóa.

d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}

[['---', '---', '---'],
    [-8522787127447073495, 'barry', 'green'],
    ['---', '---', '---'],
    ['---', '---', '---'],
    ['---', '---', '---'],
    [-9092791511155847987, 'timmy', 'red'],
    ['---', '---', '---'],
    [-6480567542315338377, 'guido', 'blue']]
4

length of the key % table_size (3)
6

Tương tự ở đây! Rất giống với phương thức

perturb = hash
perturb >>= 5
new_hash = (current_index*5 + 1 + perturb)
9 mà bạn đã thấy,
d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}

[['---', '---', '---'],
    [-8522787127447073495, 'barry', 'green'],
    ['---', '---', '---'],
    ['---', '---', '---'],
    ['---', '---', '---'],
    [-9092791511155847987, 'timmy', 'red'],
    ['---', '---', '---'],
    [-6480567542315338377, 'guido', 'blue']]
4 hiện sử dụng kiểm tra
d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}

[['---', '---', '---'],
    [-8522787127447073495, 'barry', 'green'],
    ['---', '---', '---'],
    ['---', '---', '---'],
    ['---', '---', '---'],
    [-9092791511155847987, 'timmy', 'red'],
    ['---', '---', '---'],
    [-6480567542315338377, 'guido', 'blue']]
7 đơn giản để kiểm tra xem khe cắm có trống hoặc giả không. Nếu vậy, nó trả về một
perturb = hash
perturb >>= 5
new_hash = (current_index*5 + 1 + perturb)
3. Nếu không, nó trả về giá trị từ bảng
1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
2        index = hash_ & mask
3        yield index
4        perturb = hash_
5        while True:
6            new_hash = index * 5 + 1 + perturb
7            yield new_hash & mask
8            perturb >>= 5
9.

 1    class Dictionary:
 2
 3        def __init__(self, *args, **kwargs):
 4            """Dict initializiation"""
 5
 6        @staticmethod
 7        def _generate_probes(hash_: int, mask: int) -> Iterable[int]:
 8            index = hash_ & mask
 9            yield index
10            perturb = hash_
11            while True:
12                new_hash = index * 5 + 1 + perturb
13                yield new_hash & mask
14                perturb >>= 5
15
16        def _lookup(self, key: Any, hashvalue: int) -> Tuple[int, Any]:
17            """The lookup method"""
18
19        def __getitem__(self, key: Any) -> Any:
20            """Get value from dict"""
21
22        def __setitem__(self, key: Any, value: Any):
23            """Insert item to dict"""
24
25        def __delitem__(self, key: Any):
26            """Delete item from dict"""
27
28        def __len__(self) -> int:
29            """Dict length"""
30
31        def __iter__(self) -> Iterable:
32            """Iterate through dictionary"""
33
34        def __contains__(self, key: Any) -> bool:
35            """Check if dictionary contains a key"""
36
37        def __repr__(self) -> str:
38            """Dict representation"""
6

length of the key % table_size (3)
7

  • Dòng 2 chuyển đổi từ điển thành từ điển không được chia sẻ. converts the dictionary to a non shared dictionary.

  • Dòng 6 tăng phiên bản nếu khe cắm không có người. increases the version if the slot is unoccupied.

  • Dòng 7 lấp đầy chỉ số băm trong

    d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}
    
    [['---', '---', '---'],
        [-8522787127447073495, 'barry', 'green'],
        ['---', '---', '---'],
        ['---', '---', '---'],
        ['---', '---', '---'],
        [-9092791511155847987, 'timmy', 'red'],
        ['---', '---', '---'],
        [-6480567542315338377, 'guido', 'blue']]
    
    0 với chỉ số của khóa và giá trị về cơ bản là
    length of the key % table_size (3)
    
    02 bởi vì nó là chỉ số cuối cùng của
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    8 và
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    9.
    fills the hashed index in
    d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}
    
    [['---', '---', '---'],
        [-8522787127447073495, 'barry', 'green'],
        ['---', '---', '---'],
        ['---', '---', '---'],
        ['---', '---', '---'],
        [-9092791511155847987, 'timmy', 'red'],
        ['---', '---', '---'],
        [-6480567542315338377, 'guido', 'blue']]
    
    0 with the index of the key and value which is essentially
    length of the key % table_size (3)
    
    02 because it’s the last index of
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    8 and
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    9.

  • Dòng 13 tăng

    length of the key % table_size (3)
    
    05 nếu khe cắm đó là miễn phí. Điều đó đã được thực hiện bởi vì nếu nó miễn phí, thì
    length of the key % table_size (3)
    
    05 đã bao gồm nó.
    increases
    length of the key % table_size (3)
    
    05 If that slot was free. That’s done because if it weren’t free, than
    length of the key % table_size (3)
    
    05 would have already included it.

  • Dòng 14 thay đổi kích thước bảng bằng 3 nếu nó được lấp đầy hơn 2/3. Nó theo logic của C Python thay đổi kích thước. resizes the table by 3 if it’s more than 2/3 filled. It follows the logic of C Python resize.

  • Dòng 17 kiểm tra xem giá trị mới khác với giá trị sắp được thay thế. Từ điển không tăng phiên bản nếu không có gì thay đổi. checks if the new value differs from the value that is about to be replaced. The dictionary does not increase the version if nothing changes.

 1    class Dictionary:
 2
 3        def __init__(self, *args, **kwargs):
 4            """Dict initializiation"""
 5
 6        @staticmethod
 7        def _generate_probes(hash_: int, mask: int) -> Iterable[int]:
 8            index = hash_ & mask
 9            yield index
10            perturb = hash_
11            while True:
12                new_hash = index * 5 + 1 + perturb
13                yield new_hash & mask
14                perturb >>= 5
15
16        def _lookup(self, key: Any, hashvalue: int) -> Tuple[int, Any]:
17            """The lookup method"""
18
19        def __getitem__(self, key: Any) -> Any:
20            """Get value from dict"""
21
22        def __setitem__(self, key: Any, value: Any):
23            """Insert item to dict"""
24
25        def __delitem__(self, key: Any):
26            """Delete item from dict"""
27
28        def __len__(self) -> int:
29            """Dict length"""
30
31        def __iter__(self) -> Iterable:
32            """Iterate through dictionary"""
33
34        def __contains__(self, key: Any) -> bool:
35            """Check if dictionary contains a key"""
36
37        def __repr__(self) -> str:
38            """Dict representation"""
7

length of the key % table_size (3)
8

  • Dòng 2 chuyển đổi từ điển thành từ điển không được chia sẻ. converts the dictionary to a non shared dictionary.

  • Dòng 6 tăng phiên bản nếu khe cắm không có người. raises

    perturb = hash
    perturb >>= 5
    new_hash = (current_index*5 + 1 + perturb)
    
    3 If the slot is unoccupied.

  • Dòng 7 lấp đầy chỉ số băm trong

    d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}
    
    [['---', '---', '---'],
        [-8522787127447073495, 'barry', 'green'],
        ['---', '---', '---'],
        ['---', '---', '---'],
        ['---', '---', '---'],
        [-9092791511155847987, 'timmy', 'red'],
        ['---', '---', '---'],
        [-6480567542315338377, 'guido', 'blue']]
    
    0 với chỉ số của khóa và giá trị về cơ bản là
    length of the key % table_size (3)
    
    02 bởi vì nó là chỉ số cuối cùng của
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    8 và
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    9.
    inserts a
    perturb = hash
    perturb >>= 5
    new_hash = (current_index*5 + 1 + perturb)
    
    1 value instead of the actual value.

  • Dòng 13 tăng

    length of the key % table_size (3)
    
    05 nếu khe cắm đó là miễn phí. Điều đó đã được thực hiện bởi vì nếu nó miễn phí, thì
    length of the key % table_size (3)
    
    05 đã bao gồm nó.
    : You might have thought that a
    length of the key % table_size (3)
    
    10 operation might suffice, but it would have left a hole inside the
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    8 and
    1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
    2        index = hash_ & mask
    3        yield index
    4        perturb = hash_
    5        while True:
    6            new_hash = index * 5 + 1 + perturb
    7            yield new_hash & mask
    8            perturb >>= 5
    
    9 table. These tables must not contain any holes. The solution is to swap with the last item and then delete the last item.

  • Dòng 14 thay đổi kích thước bảng bằng 3 nếu nó được lấp đầy hơn 2/3. Nó theo logic của C Python thay đổi kích thước. changes the swapped element’s indices value to the current spot that’s being swapped.

length of the key % table_size (3)
13

length of the key % table_size (3)
9

Dòng 17 kiểm tra xem giá trị mới khác với giá trị sắp được thay thế. Từ điển không tăng phiên bản nếu không có gì thay đổi.

Dòng 6 tăng
perturb = hash
perturb >>= 5
new_hash = (current_index*5 + 1 + perturb)
3 nếu khe cắm không có người ở.


>>> "{0:b}".format(hash('hash2'))
>>> '110000010001001001011000101001010110101110010010000001101111'

>>> "{0:b}".format(hash('hash'))
>>> '101110001101101111011001100001110000000110011011000110110000'

0

Dòng 10 chèn giá trị

perturb = hash
perturb >>= 5
new_hash = (current_index*5 + 1 + perturb)
1 thay vì giá trị thực.

Dòng 12: Bạn có thể đã nghĩ rằng một hoạt động length of the key % table_size (3) 10 có thể đủ, nhưng nó sẽ để lại một lỗ bên trong bảng 1 def generate_probes(hash_: int, mask: int) -> Iterable[int]: 2 index = hash_ & mask 3 yield index 4 perturb = hash_ 5 while True: 6 new_hash = index * 5 + 1 + perturb 7 yield new_hash & mask 8 perturb >>= 5 8 và 1 def generate_probes(hash_: int, mask: int) -> Iterable[int]: 2 index = hash_ & mask 3 yield index 4 perturb = hash_ 5 while True: 6 new_hash = index * 5 + 1 + perturb 7 yield new_hash & mask 8 perturb >>= 5 9. Những bảng này không được chứa bất kỳ lỗ hổng nào. Giải pháp là trao đổi với mục cuối cùng và sau đó xóa mục cuối cùng.

Dòng 18 thay đổi giá trị chỉ số của phần tử hoán đổi thành vị trí hiện tại mà Lừa được hoán đổi.


>>> "{0:b}".format(hash('hash2'))
>>> '110000010001001001011000101001010110101110010010000001101111'

>>> "{0:b}".format(hash('hash'))
>>> '101110001101101111011001100001110000000110011011000110110000'

1

Phương pháp này kiểm tra xem từ điển có chứa một khóa nhất định. Nó làm như vậy bằng cách kiểm tra kết quả của

 1    class Dictionary:
 2
 3        def __init__(self, *args, **kwargs):
 4            """Dict initializiation"""
 5
 6        @staticmethod
 7        def _generate_probes(hash_: int, mask: int) -> Iterable[int]:
 8            index = hash_ & mask
 9            yield index
10            perturb = hash_
11            while True:
12                new_hash = index * 5 + 1 + perturb
13                yield new_hash & mask
14                perturb >>= 5
15
16        def _lookup(self, key: Any, hashvalue: int) -> Tuple[int, Any]:
17            """The lookup method"""
18
19        def __getitem__(self, key: Any) -> Any:
20            """Get value from dict"""
21
22        def __setitem__(self, key: Any, value: Any):
23            """Insert item to dict"""
24
25        def __delitem__(self, key: Any):
26            """Delete item from dict"""
27
28        def __len__(self) -> int:
29            """Dict length"""
30
31        def __iter__(self) -> Iterable:
32            """Iterate through dictionary"""
33
34        def __contains__(self, key: Any) -> bool:
35            """Check if dictionary contains a key"""
36
37        def __repr__(self) -> str:
38            """Dict representation"""
8, nó chỉ chứa các chỉ số dưới 0 trong trường hợp
perturb = hash
perturb >>= 5
new_hash = (current_index*5 + 1 + perturb)
1 hoặc
 1    DUMMY = -2
 2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
 3        mask = len(table) - 1
 4        freeslot = None
 5        for index in generate_probes(hash_, mask):
 6            elem = table[index]
 7            if elem is None:
 8                return (index, None) if freeslot is None else (freeslot, DUMMY)
 9            elif elem == DUMMY:
10                if freeslot is None:
11                    freeslot = index
12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
13                return (index, elem)
5.

length of the key % table_size (3)
17


>>> "{0:b}".format(hash('hash2'))
>>> '110000010001001001011000101001010110101110010010000001101111'

>>> "{0:b}".format(hash('hash'))
>>> '101110001101101111011001100001110000000110011011000110110000'

2

Danh sách các khóa từ điển của các trường hợp giữ các trường hợp

1    def generate_probes(hash_: int, mask: int) -> Iterable[int]:
2        index = hash_ & mask
3        yield index
4        perturb = hash_
5        while True:
6            new_hash = index * 5 + 1 + perturb
7            yield new_hash & mask
8            perturb >>= 5
2. Phương pháp này tạo ra một danh sách mới về các khóa thực tế (không có lớp trình bao bọc) và kết thúc nó bên trong một điều khác.


>>> "{0:b}".format(hash('hash2'))
>>> '110000010001001001011000101001010110101110010010000001101111'

>>> "{0:b}".format(hash('hash'))
>>> '101110001101101111011001100001110000000110011011000110110000'

3

Ghi chú

Mã đầy đủ có thể được tìm thấy ở đây.

Đó là thời gian để sử dụng tất cả các phương pháp mới tuyệt vời này và làm cho từ điển của bạn có thể sử dụng được!

Nó khá giống với phương pháp

perturb = hash
perturb >>= 5
new_hash = (current_index*5 + 1 + perturb)
0 từ trước đó, chỉ lần này nó sử dụng
d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}

[['---', '---', '---'],
    [-8522787127447073495, 'barry', 'green'],
    ['---', '---', '---'],
    ['---', '---', '---'],
    ['---', '---', '---'],
    [-9092791511155847987, 'timmy', 'red'],
    ['---', '---', '---'],
    [-6480567542315338377, 'guido', 'blue']]
0 làm bảng băm,
 1    DUMMY = -2
 2    def lookup(key: Any, hash_: int) -> Tuple[int, Any]:
 3        mask = len(table) - 1
 4        freeslot = None
 5        for index in generate_probes(hash_, mask):
 6            elem = table[index]
 7            if elem is None:
 8                return (index, None) if freeslot is None else (freeslot, DUMMY)
 9            elif elem == DUMMY:
10                if freeslot is None:
11                    freeslot = index
12            elif elem.key is key or (elem.hash == hash_ and elem.key == key):
13                return (index, elem)
5 để kiểm tra các khe trống thay vì
perturb = hash
perturb >>= 5
new_hash = (current_index*5 + 1 + perturb)
2 và
1    def search(key: Any) -> Any:  # usually implemented as __getitem__
2        hashvalue = hash(key)
3        index, elem = lookup(key, hashvalue)
4        if elem is None or elem == DUMMY:
5            raise KeyError(key)
6        return table[index]
3 để kiểm tra sự bình đẳng và nhận dạng của các khóa.

Băm các lớp tùy chỉnh của bạn

Bạn muốn sử dụng một lớp tùy chỉnh làm khóa từ điển hoặc làm giá trị trong một tập hợp. Bây giờ bạn đã biết phương pháp

length of the key % table_size (3)
24 làm gì và cách thực hiện các cấu trúc này, bạn đã biết rằng bạn có thể tận dụng phương thức
length of the key % table_size (3)
24 và bạn cũng phải thực hiện
length of the key % table_size (3)
26 vì các khóa được kiểm tra sự bình đẳng. Lưu ý rằng yêu cầu các đối tượng so sánh bằng nhau có cùng giá trị băm. Bạn nên trộn lẫn các giá trị băm của các thuộc tính của đối tượng cũng đóng một phần so với các đối tượng bằng cách đóng gói chúng thành một tuple và băm tuple. Sau đây là một ví dụ về lớp whing
length of the key % table_size (3)
27 tùy chỉnh:


>>> "{0:b}".format(hash('hash2'))
>>> '110000010001001001011000101001010110101110010010000001101111'

>>> "{0:b}".format(hash('hash'))
>>> '101110001101101111011001100001110000000110011011000110110000'

4

Bạn có thể thấy rằng

length of the key % table_size (3)
24 đang được sử dụng để băm một tuple của các thuộc tính lớp tùy chỉnh, phải đủ nhanh và cùng một tuple cũng được sử dụng để kiểm tra bình đẳng.

Kiểm tra hiểu

Tại sao nó quan trọng đối với các đối tượng băm có thể chứa

length of the key % table_size (3)
26?

Hiển thị giải pháp

Bạn đã thấy việc thực hiện phương thức tra cứu, trong đó có dòng sau (dòng 63):


>>> "{0:b}".format(hash('hash2'))
>>> '110000010001001001011000101001010110101110010010000001101111'

>>> "{0:b}".format(hash('hash'))
>>> '101110001101101111011001100001110000000110011011000110110000'

5

Lưu ý rằng

length of the key % table_size (3)
30 và
length of the key % table_size (3)
31 đều là các đối tượng có thể so sánh được.

Hiểu khi nào nên sử dụng bảng băm python

Bảng băm Python (và bảng băm nói chung) không gian giao dịch theo thời gian. Sự cần thiết phải lưu trữ khóa và băm cùng với giá trị của các mục cộng với các khe trống, làm cho các bảng băm chiếm nhiều bộ nhớ hơn - nhưng cũng nhanh hơn (trong hầu hết các kịch bản).

Đặt danh sách VS

Kiểm tra hiểu

Tại sao nó quan trọng đối với các đối tượng băm có thể chứa

length of the key % table_size (3)
26?

Hiển thị giải pháp

Bạn đã thấy việc thực hiện phương thức tra cứu, trong đó có dòng sau (dòng 63):

  • Lưu ý rằng
    length of the key % table_size (3)
    
    30 và
    length of the key % table_size (3)
    
    31 đều là các đối tượng có thể so sánh được.
    : You already know that lookups are faster in sets than list - That’s the whole point of having a hash table. Iterating over a list is slighly faster than sets. Don’t take my word for it:


>>> "{0:b}".format(hash('hash2'))
>>> '110000010001001001011000101001010110101110010010000001101111'

>>> "{0:b}".format(hash('hash'))
>>> '101110001101101111011001100001110000000110011011000110110000'

6

  • Hiểu khi nào nên sử dụng bảng băm python If space is your main concern rather than speed, you are probably better off with lists - The difference is quite large.


>>> "{0:b}".format(hash('hash2'))
>>> '110000010001001001011000101001010110101110010010000001101111'

>>> "{0:b}".format(hash('hash'))
>>> '101110001101101111011001100001110000000110011011000110110000'

7

  • Bảng băm Python (và bảng băm nói chung) không gian giao dịch theo thời gian. Sự cần thiết phải lưu trữ khóa và băm cùng với giá trị của các mục cộng với các khe trống, làm cho các bảng băm chiếm nhiều bộ nhớ hơn - nhưng cũng nhanh hơn (trong hầu hết các kịch bản). Sets are not ordered, while lists are. As we’ve talked about, sets do not implement the separate dense table that dictionaries do, which results in a single unordered table.

  • Đặt danh sách VS Lists do not require their elements to be hashable as opposed to sets.

Những gì nhanh hơn, đặt hoặc một danh sách, khi kiểm tra xem có tồn tại một giá trị nhất định không?

Bởi vì các bộ được thực hiện dưới dạng bảng băm và các bảng băm làm cho tra cứu nhanh hơn nhiều vì hàm băm - một bộ sẽ nhanh hơn nhiều trong các tra cứu!

  • Tốc độ: Bạn đã biết rằng tra cứu nhanh hơn trong các bộ so với danh sách - đó là toàn bộ điểm có bảng băm. Lặp lại trong danh sách nhanh hơn các bộ. Don lồng lấy lời của tôi cho nó: Usually, you won’t have to deal with very large
    length of the key % table_size (3)
    
    32 instances nor will you iterate over them. I will however show the speed comparisons of the lookup function shown above, just in case you will.


>>> "{0:b}".format(hash('hash2'))
>>> '110000010001001001011000101001010110101110010010000001101111'

>>> "{0:b}".format(hash('hash'))
>>> '101110001101101111011001100001110000000110011011000110110000'

8

  • Không gian: Nếu không gian là mối quan tâm chính của bạn chứ không phải tốc độ, có lẽ bạn sẽ tốt hơn với danh sách - sự khác biệt là khá lớn.
    length of the key % table_size (3)
    
    32 takes drastically less space than a dictionary:


>>> "{0:b}".format(hash('hash2'))
>>> '110000010001001001011000101001010110101110010010000001101111'

>>> "{0:b}".format(hash('hash'))
>>> '101110001101101111011001100001110000000110011011000110110000'

9

  • Đặt hàng: Bộ không được đặt hàng, trong khi danh sách là. Như chúng tôi đã nói, các bộ không thực hiện bảng dày đặc riêng biệt mà từ điển làm, dẫn đến một bảng không đặt hàng. Both are ordered (since Python 3.6).

  • Băm: Danh sách không yêu cầu các yếu tố của chúng có thể băm trái ngược với các bộ.

    length of the key % table_size (3)
    
    32 instances only allow strings to be their attribute names, as opposed to dictionaries that allow everything as long as it is hashable.

Dict vs có tên

Không giống như các bộ và danh sách, từ điển và

length of the key % table_size (3)
32 khá khác nhau trong việc sử dụng của chúng. Việc sử dụng chính cho
length of the key % table_size (3)
32 là khi bạn muốn một đối tượng không thể chối cãi, dễ đọc hơn nhiều so với từ điển và đơn giản hơn một lớp. Như bạn sẽ sớm tìm ra,
length of the key % table_size (3)
32 cũng nhỏ hơn nhiều so với từ điển - vì vậy hãy thêm nó vào những cân nhắc của bạn!

Tốc độ: Thông thường, bạn đã giành chiến thắng để đối phó với các trường hợp length of the key % table_size (3) 32 rất lớn và bạn cũng sẽ không lặp lại chúng. Tuy nhiên, tôi sẽ hiển thị các so sánh tốc độ của hàm tra cứu được hiển thị ở trên, chỉ trong trường hợp bạn sẽ làm.

Không gian:

length of the key % table_size (3)
32 mất ít không gian hơn so với từ điển:

Đặt hàng: Cả hai đều được đặt hàng (kể từ Python 3.6).

Băm: length of the key % table_size (3) 32 Các trường hợp chỉ cho phép các chuỗi là tên thuộc tính của chúng, trái ngược với từ điển cho phép mọi thứ miễn là nó có thể băm.

  • Ghi chú

  • Python 3.7 đã thêm các lớp dữ liệu mà bạn có thể thấy dễ sử dụng hơn

    length of the key % table_size (3)
    
    32, mặc dù bài viết này đã giành được chúng.

  • Sự kết luận

  • Xin chúc mừng! Bây giờ bạn đã có một cái nhìn tổng quan toàn diện về các bảng Hash Python. Bạn đã học được một khái niệm quan trọng trong các bảng khoa học máy tính, cách chúng được thực hiện trong Python, tối ưu hóa tuyệt vời của từ điển Python và khi nào nên sử dụng bảng Hash Python. Bây giờ bạn biết rằng các bảng băm không gian giao dịch theo thời gian và thậm chí bạn có thể thực hành so sánh kích thước và tốc độ của các cấu trúc dữ liệu khác nhau. Tôi hy vọng rằng bạn cảm thấy tự tin hơn khi chọn cấu trúc dữ liệu cho dự án tiếp theo của bạn!

Làm thế nào để một từ điển trong Python hoạt động?

Từ điển sử dụng hàm băm của mỗi khóa để thay đổi một số thông tin khóa thành một số nguyên được gọi là giá trị băm. Giá trị băm cho bạn biết nhóm nào mà cặp giá trị khóa đó nên được đặt vào. Bằng cách này mỗi khi bạn cần tra cứu hoặc tìm cặp giá trị khóa đó, bạn biết chính xác nhóm nào sẽ tìm kiếm.uses each key's hash function to change some of the key's information into an integer known as a hash value. The hash value tells you which bucket that key-value pair should be placed in. This way every time you need to lookup or find that key-value pair, you know exactly which bucket to search.

Làm thế nào để một từ điển hoạt động trong nội bộ?

Từ điển bao gồm một số thùng.Mỗi thùng này chứa mã băm của đối tượng chứa cặp giá trị khóa.Một con trỏ tới đối tượng chính và một con trỏ tới đối tượng giá trị.

Làm thế nào để một từ điển hoạt động?

Một từ điển có một bộ các khóa và mỗi khóa có một giá trị liên quan duy nhất.Khi được trình bày với một khóa, từ điển sẽ trả về giá trị liên quan.Một từ điển còn được gọi là băm, bản đồ, một hashmap trong các ngôn ngữ lập trình khác nhau (và một đối tượng trong JavaScript).Tất cả đều giống nhau: một cửa hàng giá trị khóa.When presented with a key, the dictionary will return the associated value. A dictionary is also called a hash, a map, a hashmap in different programming languages (and an Object in JavaScript). They're all the same thing: a key-value store.

Từ điển được viết bằng Python như thế nào?

Từ điển được viết bằng dấu ngoặc xoăn ({}), bao gồm các cặp giá trị khóa được phân tách bằng dấu phẩy (,).Một dấu hai chấm (:) tách từng khóa với giá trị của nó.with curly brackets ({}), including key-value pairs separated by commas (,). A colon (:) separates each key from its value.