Câu hỏi Kiểm tra xem một khóa đã cho đã tồn tại trong từ điển chưa


Tôi muốn kiểm tra xem một khóa có tồn tại trong từ điển hay không trước khi cập nhật giá trị cho khóa. Tôi đã viết đoạn mã sau:

if 'key1' in dict.keys():
  print "blah"
else:
  print "boo"

Tôi nghĩ đây không phải là cách tốt nhất để hoàn thành nhiệm vụ này. Có cách nào tốt hơn để kiểm tra một khóa trong từ điển không?


2189
2017-10-21 19:05


gốc


Đang gọi dict.keys() tạo danh sách khóa, theo tài liệu docs.python.org/2/library/stdtypes.html#dict.keys nhưng tôi sẽ ngạc nhiên nếu mô hình này không được tối ưu hóa, trong một triển khai nghiêm túc, để dịch sang if 'key1' in dict:. - Evgeni Sergeev
Vì vậy, cuối cùng tôi đã tìm ra lý do tại sao nhiều kịch bản Python của tôi quá chậm :) :( Đó là vì tôi đã sử dụng x in dict.keys() để kiểm tra các phím. Và điều đó xảy ra bởi vì cách thông thường để lặp qua các khóa trong Java là for (Type k : dict.keySet()), thói quen này gây ra for k in dict.keys() cảm thấy tự nhiên hơn for k in dict (mà vẫn nên tốt về hiệu suất?), nhưng sau đó kiểm tra các phím trở thành if k in dict.keys() quá, đó là một vấn đề ... - Evgeni Sergeev
@EvgeniSergeev if k in dict_: kiểm tra sự hiện diện của k trong KEYS của dict_, vì vậy bạn vẫn không cần dict_.keys(). (Điều này có chút tôi, vì nó đọc cho tôi như thử nghiệm của nó cho một giá trị trong dict. Nhưng nó không phải là.) - ToolmakerSteve
@ToolmakerSteve Đúng vậy, nhưng không chỉ bạn không cần nó, nó không phải là một thực hành tốt. - Evgeni Sergeev
Hãy thử "chìa khóa trong dict" - marcelosalloum


Các câu trả lời:


in là cách dự định để kiểm tra sự tồn tại của khóa trong dict.

d = dict()

for i in xrange(100):
    key = i % 10
    if key in d:
        d[key] += 1
    else:
        d[key] = 1

Nếu bạn muốn mặc định, bạn luôn có thể sử dụng dict.get():

d = dict()

for i in xrange(100):
    key = i % 10
    d[key] = d.get(key, 0) + 1

... và nếu bạn muốn luôn đảm bảo giá trị mặc định cho bất kỳ khóa nào bạn có thể sử dụng defaultdict từ collections mô-đun, như vậy:

from collections import defaultdict

d = defaultdict(lambda: 0)

for i in xrange(100):
    d[i % 10] += 1

... nhưng nói chung, in từ khóa là cách tốt nhất để làm điều đó.


2242
2017-10-21 19:10



Tôi thường chỉ sử dụng get nếu tôi định kéo item ra khỏi từ điển. Không có ý nghĩa trong việc sử dụng in  và kéo vật phẩm ra khỏi từ điển. - Jason Baker
Tôi hoàn toàn đồng ý. Nhưng nếu bạn chỉ cần biết liệu khóa có tồn tại hay bạn cần phân biệt giữa trường hợp khóa được xác định và trường hợp bạn đang sử dụng mặc định, in là cách tốt nhất để làm điều đó. - Chris B.
Tài liệu tham khảo cho câu trả lời này là tài liệu python - enkash
nhận được là một thử nghiệm xấu nếu khóa tương đương với "False", như 0 ví dụ. Đã học được điều này một cách khó khăn: / - Sebastien
Tôi không thể đồng ý rằng đây là một câu trả lời hoàn chỉnh vì nó không đề cập đến 'try' - 'except' sẽ là nhanh nhất khi số lượng khóa thất bại là đủ nhỏ. Xem câu trả lời dưới đây: stackoverflow.com/a/1602945/4376643 - Craig Hicks


Bạn không phải gọi các phím:

if 'key1' in dict:
  print "blah"
else:
  print "boo"

Điều đó sẽ nhiều nhanh hơn vì nó sử dụng băm của từ điển trái ngược với việc thực hiện tìm kiếm tuyến tính, mà các phím gọi sẽ thực hiện.


1099
2017-10-21 19:06



Cái đó thật tuyệt. Tôi đã được ấn tượng rằng nó sẽ vẫn trong nội bộ vẫn đi qua danh sách các phím, nhưng tôi thấy điều này hoạt động giống như thử nghiệm thành viên trong một bộ. - Mohan Gulati
@ Mohan Gulati: Bạn hiểu rằng một từ điển là một hashtable của các phím ánh xạ tới các giá trị, phải không? Thuật toán băm chuyển đổi khóa thành số nguyên và số nguyên được sử dụng để tìm vị trí trong bảng băm khớp với nhau. en.wikipedia.org/wiki/Hash_table - hughdbrown
@Charles Addis, từ kinh nghiệm làm việc với khoảng nửa triệu phím bạn nhận được ít nhất 10x hiệu suất tăng khi viết "chìa khóa trong dict" thay vì "chìa khóa trong dict.keys ()". PEP và Zen cũng nói rằng bạn nên bỏ qua chúng trong trường hợp chúng xấu cho dự án của bạn. - ivan_bilan
ivan_bilan - Tôi chỉ chạy cái băng ghế của riêng mình về điều này ... Trên nửa triệu chìa khóa, if key in d1 lấy 0.17265701293945312 giây. Đang gọi if key in d1.keys() lấy 0.23871088027954102 - đây là định nghĩa cổ điển về tối ưu hóa vi mô. Tiết kiệm 0.07884883880615234 giây không phải là tăng hiệu suất. - Charles Addis
@Eli Chỉ cần cho bạn tôi đã tạo ra một bài kiểm tra bạn có thể tự chạy. Kết quả có thể làm bạn kinh ngạc. Đối với dicts có ~ 50.000 khóa, không gọi keys() cung cấp cho bạn .01 lợi ích tính toán thứ hai. Đối với ~ 500.000 khóa, không gọi keys() mang lại cho bạn .1 lợi ích thứ hai. Cho ~ 5.000.000 khóa, không gọi keys() nhanh hơn .4 giây, nhưng với 50.000.000 khóa GỌI keys() IS 3 SECONDS nhanh hơn! - Charles Addis


Bạn có thể kiểm tra sự hiện diện của một khóa trong từ điển, bằng cách sử dụng trong từ khóa:

d = {'a': 1, 'b': 2}
'a' in d # <== evaluates to True
'c' in d # <== evaluates to False

Cách sử dụng phổ biến để kiểm tra sự tồn tại của một khóa trong từ điển trước khi biến đổi nó là mặc định khởi tạo giá trị (ví dụ: nếu giá trị của bạn là danh sách, và bạn muốn đảm bảo rằng có một danh sách trống mà bạn có thể thêm vào khi chèn giá trị đầu tiên cho một khóa). Trong những trường hợp như vậy, bạn có thể tìm thấy collections.defaultdict() loại được quan tâm.

Trong mã cũ hơn, bạn cũng có thể tìm thấy một số cách sử dụng has_key(), một phương pháp không dùng nữa để kiểm tra sự tồn tại của các khóa trong từ điển (chỉ cần sử dụng key_name in dict_name, thay thế).


226
2017-10-21 19:16



dict.has_key (khóa) đã không được dùng để ủng hộ khóa trong dict - David Locke
Về mặt kỹ thuật, has_key Là không dùng nữa cho Python 2.x + (không chỉ đơn thuần là 3,0+). Đó là, mã mới được khuyến khích không sử dụng nó, ngay cả khi viết bằng Python 2.x. (Bởi vì nó là một tính năng được biết là sẽ biến mất trong các phiên bản tương lai, và thay vào đó có một thay thế hoàn toàn tốt để sử dụng.) Điều gì xảy ra trong 3.0 là nó được loại bỏ hoàn toàn. - ToolmakerSteve
@ToolmakerSteve Bạn tất nhiên là chính xác và tôi đã cập nhật câu trả lời để phản ánh điều đó. :) - kqr
Muốn chia sẻ điều đó (sử dụng Python 2.7) thời gian chạy của một cái gì đó tôi vừa viết, dựa trên dicts, là 363.235070 bằng cách sử dụng "key in dict.keys ()" và giảm mạnh xuống 0.260186 chỉ bằng cách xóa cuộc gọi cho "keys" ) " - Ido_f
@Ido_f vui lòng đăng điểm chuẩn của bạn, vì điểm chuẩn của tôi hầu như không có sự khác biệt trong 3,5 và 2,7 - Charles Addis


Bạn có thể rút ngắn điều này:

if 'key1' in dict:
    ...

Tuy nhiên, điều này là tốt nhất một cải tiến thẩm mỹ. Tại sao bạn tin rằng đây không phải là cách tốt nhất?


74
2017-10-21 19:06



Đây là nhiều nhiều hơn một cải tiến thẩm mỹ. Thời gian để tìm một khóa bằng cách sử dụng phương pháp này là O (1) trong khi các phím gọi sẽ tạo ra một danh sách và là O (n). - Jason Baker
O (1) có vẻ không đúng. Bạn có chắc nó không giống như O (log n)? - spectras
Đó là sự phức tạp của một tra cứu dict duy nhất, đó là trên trung bình O (1) và tồi tệ nhất O (n). .list () sẽ luôn là O (n). wiki.python.org/moin/TimeComplexity - Leo Tindall


Tôi khuyên bạn nên sử dụng setdefault thay vào đó. Có vẻ như nó sẽ làm mọi thứ bạn muốn.

>>> d = {'foo':'bar'}
>>> q = d.setdefault('foo','baz') #Do not override the existing key
>>> print q #The value takes what was originally in the dictionary
bar
>>> print d
{'foo': 'bar'}
>>> r = d.setdefault('baz',18) #baz was never in the dictionary
>>> print r #Now r has the value supplied above
18
>>> print d #The dictionary's been updated
{'foo': 'bar', 'baz': 18}

40
2017-10-21 19:07



Cái gì setdefault phải làm gì với câu hỏi của OP? - hughdbrown
@hughdbrown "Tôi muốn kiểm tra xem một khóa có tồn tại trong từ điển trước khi cập nhật giá trị cho khóa hay không." Đôi khi các bài đăng bao gồm mã tạo ra một loạt các câu trả lời cho một thứ không hoàn toàn là mục tiêu ban đầu. Để thực hiện mục tiêu được nêu trong câu đầu tiên, setdefault là phương pháp hiệu quả nhất, mặc dù nó không phải là một thay thế drop-in cho mã mẫu được đăng. - David Berger
Đây là câu trả lời vượt trội vì nó thỏa mãn mục tiêu của OP thay vì chỉ đưa ra câu trả lời đúng về mặt kỹ thuật. Xem: nedbatchelder.com/blog/201207/… - Niels Bom
1 cho một câu trả lời thông tin, mà đã dạy tôi một cái gì đó. Tuy nhiên, cho dù đó là giải pháp tốt nhất phụ thuộc vào những gì các coder có trong tâm trí; ví dụ. ý nghĩa của "trước khi cập nhật giá trị của khóa". Có lẽ anh ta sẽ ném một ngoại lệ nếu nó không có mặt (== không được phép thêm khóa mới). Có lẽ đó là một từ điển đếm, và anh ta sẽ thêm 1 vào số lượng hiện có, trong trường hợp đó `d [key] = d.get (khóa, 0) + 1 'là giải pháp sạch nhất (như Chris cho thấy, sau câu trả lời của bạn đã được viết). (Tôi chỉ bận tâm nhắc đến điều này, trong trường hợp các độc giả tương lai đến đây, với các nhiệm vụ khác nhau trong tâm trí.) - ToolmakerSteve
@NielsBom ... IMHO setdefault là chỉ có các cấp trên giải pháp khi một mục nhập hiện có nên không phải được ghi đè. (Một trường hợp quan trọng, nhưng không phải là lý do duy nhất để kiểm tra sự tồn tại của khóa.) - ToolmakerSteve


Để biết thêm thông tin về việc thực hiện tốc độ các phương pháp được đề xuất của câu trả lời được chấp nhận (vòng 10m):

  • 'key' in mydict thời gian trôi qua 1.07 giây
  • mydict.get('key') thời gian trôi qua 1.84 giây
  • mydefaultdict['key'] thời gian trôi qua 1.07 giây

Do đó sử dụng in hoặc là defaultdict được đề nghị chống lại get.


35
2018-05-29 11:06



nhận được về bản chất là sự kết hợp của các điểm bullet 1 và 3 .. - scape
hoàn toàn đồng ý rằng get1.84s là <1.07 * 2 ;-P - Paul Rigor


Từ điển trong python có phương thức get ('key', default). Vì vậy, bạn chỉ có thể đặt giá trị mặc định trong trường hợp không có khóa.

values = {...}
myValue = values.get('Key', None)

19
2018-03-01 09:03