Tại sao dict.get (key) hoạt động nhưng không dict [key]?


17

Tôi đang cố gắng nhóm các chuỗi nhị phân của một số số nhất định dựa trên số chuỗi có 1.

Điều này không hoạt động:

s = "0 1 3 7 8 9 11 15"
numbers = map(int, s.split())
binaries = [bin(x)[2:].rjust(4, '0') for x in numbers]

one_groups = dict.fromkeys(range(5), [])
for x in binaries:
    one_groups[x.count('1')] += [x]

Từ điển dự kiến one_groupscần phải được

{0: ['0000'], 
 1: ['0001', '1000'], 
 2: ['0011', '1001'], 
 3: ['0111', '1011'], 
 4: ['1111']}

Nhưng tôi hiểu

{0: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 1: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 2: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 3: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 4: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111']}

Cho đến nay, điều duy nhất đã làm việc là nếu tôi sử dụng one_groups[x.count('1')] = one_groups.get(x.count('1')) + [x]thay vìone_groups[x.count('1')] += [x]

Nhưng tại sao lại như vậy? Nếu tôi nhớ lại một cách chính xác, không dict[key]phải trả lại giá trị của từ điển đó, tương tự như cách dict.get(key)hoạt động? Tôi đã thấy chủ đề này Tại sao dict.get (khóa) thay vì dict [key]? nhưng nó không trả lời câu hỏi của tôi cho trường hợp cụ thể này, vì tôi biết chắc chắn chương trình không có nghĩa là để có đượcKeyError

Tôi cũng đã thử one_groups[x.count('1')].append(x)nhưng điều này cũng không hiệu quả.


8
gettrả về Nonenếu khóa không tồn tại hoặc bất kỳ giá trị mặc định nào được cung cấp, trong khi toán tử chỉ mục []đưa ra lỗi nếu khóa không tồn tại.
adnanmuttaleb

Sidenote, bin(x)[2:].rjust(4, '0')có thể được đơn giản hóa để '{:0>4b}'.format(x).
wjandrea

1
BTW nó giúp tạo ra một ví dụ tái sản xuất tối thiểu . Trong trường hợp này, cách bạn thực hiện binarieskhông liên quan đến câu hỏi, vì vậy bạn chỉ có thể cung cấp giá trị của nó.
wjandrea

1
Điều này có trả lời câu hỏi của bạn không? dict.fromkeys đều trỏ đến cùng một danh sách
Georgy

Câu trả lời:


24

Vấn đề là tính đột biến:

one_groups = dict.fromkeys(range(5), [])- điều này vượt qua cùng một danh sách như giá trị cho tất cả các khóa . Vì vậy, nếu bạn thay đổi một giá trị, bạn thay đổi tất cả.

Về cơ bản nó giống như nói:

tmp = []
one_groups = dict.fromkeys(range(5), tmp)
del tmp

Nếu bạn muốn sử dụng một danh sách mới, bạn cần thực hiện nó trong một vòng lặp - hoặc là một forvòng lặp rõ ràng hoặc trong một sự hiểu biết chính tả:

one_groups = {key: [] for key in range(5)}

Điều này sẽ "thực thi" [](bằng với list()) cho mọi khóa, do đó tạo ra các giá trị với các danh sách khác nhau.


Tại sao getlàm việc? Bởi vì bạn rõ ràng lấy danh sách hiện tại, nhưng +tạo một danh sách kết quả mới. Và nó không quan trọng dù nó one_groups[x.count('1')] = one_groups.get(x.count('1')) + [x]hay one_groups[x.count('1')] = one_groups[x.count('1')] + [x]- điều quan trọng là có +.

Tôi biết làm thế nào tất cả mọi người nói a+=bchỉ là a=a+b, nhưng việc thực hiện có thể khác nhau để tối ưu hóa - trong trường hợp danh sách, +=chỉ là .extendbởi vì chúng ta biết chúng ta muốn kết quả của chúng tôi trong biến hiện nay, vì vậy tạo danh sách mới sẽ là sự lãng phí bộ nhớ.


À, vâng, đã hiểu. Tôi cũng nhớ có một vấn đề tương tự khi tôi muốn tạo một danh sách 2D bằng cách sử dụng mylist = [[] * 5] * 5và cách mylist = [[] for x in range(5)] * 5khắc phục nó. Chỉ cần làm rõ nhanh chóng, từ tôi đã hiểu, điều này xảy ra do các biến chỉ đến địa chỉ bộ nhớ của danh sách trống đó. Điều này cũng có nghĩa là vấn đề sẽ không xảy ra nếu tôi sử dụng nguyên thủy thay thế?
SpectraXCD

1
Có, nếu bạn đã sử dụng nguyên thủy, điều này sẽ giải quyết nó, nhưng sẽ bị hỏng one_groups[x.count('1')] += [x]vì bạn không thể thêm danh sách vào loại nguyên thủy. Một giải pháp tốt hơn là sử dụng defaultdict thay thế.
Fakher Mokadem

4
cụ thể, +gọi __add__và trả về một đối tượng mới, trong khi +=gọi __iadd__và không bắt buộc phải trả lại một đối tượng mới
njzk2

8

Vấn đề là sử dụng one_groups = dict.fromkeys(range(5), [])

(Điều này chuyển cùng một danh sách dưới dạng giá trị cho tất cả các khóa. Vì vậy, nếu bạn thay đổi một giá trị, bạn thay đổi tất cả chúng)


Bạn có thể sử dụng điều này thay thế: one_groups = {i:[] for i in range(5)}

(Điều này sẽ "thực thi" [] (bằng với danh sách ()) cho mọi khóa, do đó tạo các giá trị với các danh sách khác nhau.)


6
Bạn hoàn toàn đúng, mặc dù một lời giải thích sẽ thực sự hữu ích. Nó thực sự không phải là không biết sự khác biệt giữa hai dòng là gì.
Simon Fink

Vâng, đó là xấu của tôi. xin lỗi
Hameda169

4

Đây là sự giúp đỡ về fromkeysphương pháp của dict .

Trợ giúp về chức năng tích hợp từ khóa:

fromkey (iterable, value = none, /) của phương thức buildins.type Tạo một từ điển mới với các khóa từ iterable và các giá trị được đặt thành giá trị

Điều đó nói rằng fromkey sẽ chấp nhận một giá trị và thậm chí nó có thể gọi được, nó sẽ đánh giá nó trước, sau đó gán giá trị đó cho tất cả các khóa dict.

Danh sách có thể thay đổi trong Python và do đó, nó sẽ gán cùng một tham chiếu danh sách trống và một thay đổi sẽ ảnh hưởng đến tất cả.

Sử dụng defaultdict thay vì vậy:

>>> from collections import defaultdict
>>> one_groups = defaultdict(list)
>>> for x in binaries:
      one_groups[x.count('1')] += [x]
>>> one_groups = dict(one_groups) # to stop default dict behavior

Điều này sẽ chấp nhận các bài tập cho các khóa và giá trị không tồn tại sẽ mặc định thành các danh sách trống (trong trường hợp này).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.