2017-06-26 37 views
6

Kết quả củaSửa đổi từ điển trong khi lặp lại từ điển. Lỗi trong Python dict?

d = {1: 1} 
for k in d.keys(): 
    d['{}'.format(k)] = d.pop(k) 
print(d) 

{'1': 1}. Đầu ra của

d = {1: 1} 
for k in d.keys(): 
    d['i{}'.format(k)] = d.pop(k) 
print(d) 

{'iiiii1': 1}. Đây có phải là một lỗi? Tôi đang chạy Python 3.6.1 :: Anaconda 4.4.0 (x86_64).

+9

Tại sao đó lại là lỗi? Bạn đang xóa và thêm các phím * khi bạn lặp *. Bạn may mắn bạn đã không kết thúc trong một vòng lặp vô tận. –

+3

Không bao giờ * thay đổi * một bộ sưu tập trong khi * lặp * qua nó. Đặc biệt không phải từ điển, vì nó có thể dẫn đến hành vi kỳ lạ. –

+0

@ user2725109: làm thế nào để bạn biết rằng vòng đầu tiên chỉ chạy một lần? Đối với tất cả các bạn biết nó chạy 1000 lần. –

Trả lời

13

Không, đây không phải là lỗi. Đây là thực tế explicitly documented:

Keys và các giá trị được lặp qua trong một trật tự tùy ý đó là không ngẫu nhiên, thay đổi qua việc triển khai Python, và phụ thuộc vào lịch sử của từ điển của chèn và xóa bỏ. Nếu các khóa, giá trị và các mục xem được lặp lại mà không có sửa đổi can thiệp vào từ điển, thứ tự của các mục sẽ tương ứng trực tiếp.

[...]

lần xem lặp lại trong khi thêm hoặc xóa các mục trong từ điển có thể nâng cao một RuntimeError hoặc không để lặp qua tất cả các mục.

Mỏ nhấn mạnh đậm.

Bạn đang lặp qua các phím, trong khi đồng thời cả việc thêm và xóa các mục nhập trong từ điển. Điều đó làm việc cho một vài lần lặp lại và sau đó bạn nhấn không lặp lại được tất cả các mục nhập trường hợp và lặp lại bị dừng.

Điều gì xảy ra là bạn kích hoạt lại kích thước tại 6 bổ sung và điều đó làm cho lặp lại không thành công tại thời điểm đó; phím 'tiếp theo' hiện được đánh dấu trong một khe 'trước đó'. Điều này xảy ra cho cả bài kiểm tra, bạn chỉ không nhận ra nó lặp 5 lần trong cả hai trường hợp:

>>> d = {1: 1} 
>>> for i, k in enumerate(d): 
...  print(i) 
...  d['{}'.format(k)] = d.pop(k) 
... 
0 
1 
2 
3 
4 
>>> d = {1: 1} 
>>> for i, k in enumerate(d): 
...  print(i) 
...  d['i{}'.format(k)] = d.pop(k) 
... 
0 
1 
2 
3 
4 

Nó được chạy 5 lần vì dict thực hiện bắt đầu với một hash table of size 8, và thay đổi kích thước được kích hoạt when the table is 2/3s full (dict ban đầu của bạn có 1 mục nhập, 5 chèn nó làm cho nó > (8 * 2/3 == 5.333333). Bảng được điền đầy DKIX_DUMMY entities, được nhập khi bạn xóa một khóa (cần thiết để xử lý các va chạm băm chính xác)

Lưu ý rằng đây là tất cả phụ thuộc thực hiện cao. Trong Python 3.5 và trước đó, cả hai đoạn mã đều lặp lại một lần (ngay cả khi bạn sử dụng for k in d: để tránh tạo đối tượng danh sách cho các phím); lặp lại tiếp tục trong 3.6 bởi vì việc thực hiện thay đổi và lặp lại bây giờ theo thứ tự chèn. Các phiên bản Python tương lai là miễn phí để thay đổi việc thực hiện lại.

Các vấn đề liên quan