Xóa một mục khỏi từ điển khi khóa của nó không xác định


112

Cách tốt nhất để xóa một mục khỏi từ điển theo giá trị, tức là khi khóa của mục đó không xác định là gì? Đây là một cách tiếp cận đơn giản:

for key, item in some_dict.items():
    if item is item_to_remove:
        del some_dict[key]

Có những cách nào tốt hơn? Có gì sai với việc thay đổi (xóa các mục) khỏi từ điển trong khi lặp lại nó không?


1
Gạch chân lý do cấm sửa đổi dict trong khi lặp lại nó là vì bên trong có một thứ tự để lặp, nếu bạn thay đổi các khóa, thứ tự sẽ bị phá hủy, dẫn đến hành vi không xác định.
Spectral

Câu trả lời:


92

Lưu ý rằng bạn hiện đang kiểm tra nhận dạng đối tượng ( ischỉ trả về Truenếu cả hai toán hạng được biểu diễn bởi cùng một đối tượng trong bộ nhớ - điều này không phải lúc nào cũng xảy ra với hai đối tượng so sánh bằng ==). Nếu bạn đang cố tình làm điều này, thì bạn có thể viết lại mã của mình dưới dạng

some_dict = {key: value for key, value in some_dict.items() 
             if value is not value_to_remove}

Nhưng điều này có thể không làm những gì bạn muốn:

>>> some_dict = {1: "Hello", 2: "Goodbye", 3: "You say yes", 4: "I say no"}
>>> value_to_remove = "You say yes"
>>> some_dict = {key: value for key, value in some_dict.items() if value is not value_to_remove}
>>> some_dict
{1: 'Hello', 2: 'Goodbye', 3: 'You say yes', 4: 'I say no'}
>>> some_dict = {key: value for key, value in some_dict.items() if value != value_to_remove}
>>> some_dict
{1: 'Hello', 2: 'Goodbye', 4: 'I say no'}

Vì vậy, bạn có thể muốn !=thay vì is not.


2
Đó có phải là một nén từ điển? Chúng được thêm vào khi nào?
Buttons840

4
bạn có thể sử dụng some_dict.iteritems()ở đây và đặt forvà các ifcâu lệnh trên các dòng riêng biệt để dễ đọc
jfs

3
Tôi tin rằng khả năng hiểu từ điển đã được thêm vào Python 2.7.
mithrandi

2
@JF Sebastian: Tôi đang sử dụng Python 3 và iteritemshiện tại items. Trong Python 2.7, iteritems()thực sự là tốt hơn.
Tim Pietzcker

1
@ Buttons840 chúng được gọi là hiểu chính tả trong PEP 274 hoặc hiển thị từ điển . như PEP cho biết chúng đã được thêm vào 2.7 như là những thành tích 3.x được hỗ trợ. cách khác, bạn có thể cấp dữ liệu dict()bằng biểu thức trình tạo thích hợp, là 2,4. meta: có thể duyệt các peps ở đây để tìm kiếm nội dung.
n611x007,

120

Các dict.pop(key[, default])phương pháp cho phép bạn xoá các mục khi bạn biết chìa khóa. Nó trả về giá trị tại khóa nếu nó loại bỏ mục, ngược lại nó trả về giá trị được truyền dưới dạng default. Xem tài liệu . '

Thí dụ:

>>> dic = {'a':1, 'b':2}
>>> dic
{'a': 1, 'b': 2}
>>> dic.pop('c', 0)
0
>>> dic.pop('a', 0)
1
>>> dic
{'b': 2}

4
OP hỏi về thời điểm không xác định được khóa
nmz787,

52
a = {'name': 'your_name','class': 4}
if 'name' in a: del a['name']

OP đã hỏi về thời điểm khóa không xác định. Câu trả lời này giả định rằng chìa khóa đã được biết.
Jean-François Corbett

42

Một so sánh đơn giản giữa delpop () :

import timeit
code = """
results = {'A': 1, 'B': 2, 'C': 3}
del results['A']
del results['B']
"""
print timeit.timeit(code, number=100000)
code = """
results = {'A': 1, 'B': 2, 'C': 3}
results.pop('A')
results.pop('B')
"""
print timeit.timeit(code, number=100000)

kết quả:

0.0329667857143
0.0451040902256

Vì vậy, del nhanh hơn pop () .


6
Tuy nhiên, sự khác biệt về hiệu suất là không lớn và nếu bạn muốn tránh đưa ra một ngoại lệ, bạn có thể cung cấp đối số thứ hai pop()(như @ n-1-1 ở trên) - đây không phải là một tùy chọn cho deltoán tử.
Alex Dupuy,

1
Phụ trợ cho câu hỏi, nhưng tôi cũng đang đấu tranh để hiểu timeit. Cảm ơn bạn vì ví dụ rõ ràng này.
Adam_G

OP đã hỏi về thời điểm khóa không xác định. Câu trả lời này giả định rằng chìa khóa đã được biết.
Jean-François Corbett

7

items()trả về một danh sách và đó là danh sách mà bạn đang lặp lại, vì vậy việc thay đổi dict trong vòng lặp không quan trọng ở đây. Nếu bạn đang sử dụng iteritems()thay thế, việc thay đổi dict trong vòng lặp sẽ có vấn đề và tương tự như vậy đối với viewitems()Python 2.7.

Tôi không thể nghĩ ra cách tốt hơn để xóa các mục khỏi một mệnh lệnh theo giá trị.


7

Tôi sẽ xây dựng một danh sách các khóa cần xóa, sau đó xóa chúng. Nó đơn giản, hiệu quả và tránh bất kỳ vấn đề nào về việc đồng thời lặp lại và thay đổi lệnh.

keys_to_remove = [key for key, value in some_dict.iteritems()
                  if value == value_to_remove]
for key in keys_to_remove:
    del some_dict[key]

OP đã hỏi về thời điểm khóa không xác định. Câu trả lời này giả định rằng chìa khóa đã được biết.
Jean-François Corbett


1
y={'username':'admin','machine':['a','b','c']}
if 'c' in y['machine'] : del y['machine'][y['machine'].index('c')]

0

Không có gì sai khi xóa các mục khỏi từ điển trong khi lặp lại, như bạn đã đề xuất. Hãy cẩn thận với nhiều chủ đề sử dụng cùng một từ điển cùng một lúc, điều này có thể dẫn đến Lỗi KeyError hoặc các sự cố khác.

Tất nhiên, hãy xem tài liệu tại http://docs.python.org/library/stdtypes.html#typesmapping


for k,v in d.iteritems(): del d[k]sẽ cho RuntimeError: dictionary changed size during iteration. Xem lời giải thích của mithrandi.
Buttons840

1
Tất nhiên, d.iteritems () không phải là cách người đăng ban đầu lặp lại và không phải là những gì tôi đã đề cập đến trong câu trả lời của mình.
Thane Anthem

0

Đây là cách tôi sẽ làm điều đó.

for key in some_dict.keys():
    if some_dict[key] == item_to_remove:
        some_dict.pop(key)
        break
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.