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


2683

Tôi muốn kiểm tra nếu một khóa tồn tại trong từ điển 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ĩ rằng đâ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?


31
Việc gọi dict.keys()tạo ra một danh sách các khóa, theo tài liệu docs.python.org/2/l Library / stdtypes.html#dict.keys nhưng tôi rất ngạc nhiên nếu mẫu này không được tối ưu hóa, trong một triển khai nghiêm túc, để dịch để if 'key1' in dict:.
Evgeni Sergeev

7
Vì vậy, tôi cuối cùng đã phát hiện ra lý do tại sao rất 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 vì cách thông thường để lặp qua các phím trong Java được 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(điều vẫn còn tốt về mặt hiệu suất?), nhưng sau đó kiểm tra các phím if k in dict.keys()cũng trở thành vấn đề ...
Evgeni Sergeev

4
@EvgeniSergeev if k in dict_:kiểm tra sự hiện diện của k trong các KEYS của dict_, vì vậy bạn vẫn không cần dict_.keys(). (Điều này có bit 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 16/12/13

1
@ToolmakerSteve Điều đó đúng, 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 17/12/13

26
Hãy thử "key in dict"
marcelosalloum

Câu trả lời:


3372

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

d = {"key1": 10, "key2": 23}

if "key1" in d:
    print("this will execute")

if "nonexistent key" in d:
    print("this will not")

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 range(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 dict.setdefault()nhiều lần hoặc defaultdicttừ collectionsmô-đun, như vậy:

from collections import defaultdict

d = defaultdict(int)

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

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


74
Tôi thường chỉ sử dụng getnếu tôi sẽ rút mục ra khỏi từ điển. Không có ý nghĩa trong việc sử dụng in kéo các mục ra khỏi từ điển.
Jason Baker

75
Tôi hoàn toàn đồng ý. Nhưng nếu bạn chỉ cần biết liệu có tồn tại khóa 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, inlà cách tốt nhất để thực hiện.
Chris B.

5
Tham khảo cho câu trả lời này là tại tài liệu python
enkash

30
get là một bài kiểm tra tồi nếu khóa tương đương với "Sai", chẳng 0hạn như. Đã học được điều này một cách khó khăn: /
Sebastien

4
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 rằng 'thử' - 'ngoại trừ' sẽ là nhanh nhất khi số lượng khóa bị lỗi là đủ nhỏ. Xem câu trả lời dưới đây: stackoverflow.com/a/1602945/4376643
Craig Hicks

1546

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

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

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


7
Điều đó thật tuyệt. Tôi có ấn tượng rằng bên trong nó vẫn sẽ duyệt qua danh sách các khóa, nhưng tôi thấy điều này hoạt động giống như kiểm tra tư cách thành viên trong một bộ.
Mohan Gulati

51
@Mohan Gulati: Bạn hiểu rằng một từ điển là một hàm băm của các khóa được ánh xạ tới các giá trị, phải không? Một thuật toán băm chuyển đổi khóa thành một số nguyên và số nguyên được sử dụng để tìm một vị trí trong bảng băm phù hợp. vi.wikipedia.org/wiki/Hash_table
hughdbrown

5
@Charles Addis, từ kinh nghiệm làm việc với khoảng nửa triệu khóa, bạn sẽ tăng ít nhất 10 lần hiệu suất khi viết "key in dict" thay vì "key in 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 không tốt cho dự án của bạn.
ivan_bilan

11
ivan_bilan - Tôi vừa mới chạy thử nghiệm của riêng mình về điều này ... Trên nửa triệu chìa khóa, if key in d1mất 0.17265701293945312vài giây. Gọi if key in d1.keys()đã thực hiện 0.23871088027954102- đây là định nghĩa cổ điển của tối ưu hóa vi mô. Tiết kiệm 0.07884883880615234giây không phải là một hiệu suất tăng.
Charles Addis

11
@Eli Chỉ dành cho bạn Tôi đã tạo một bài kiểm tra bạn có thể tự chạy. Kết quả có thể làm bạn ngạc nhiên. Đối với các ký tự có ~ 50.000 khóa, việc không gọi keys()sẽ mang lại cho bạn 0,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. Đối với ~ 5.000.000 phím, không gọi keys()nhanh hơn .4 giây, nhưng với 50.000.000 phím GỌI keys()LÀ 3 GIÂY NHANH CHÓNG!
Charles Addis

268

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

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

Một cách sử dụng phổ biến để kiểm tra sự tồn tại của khóa trong từ điển trước khi thay đổi nó là khởi tạo mặc định giá trị (ví dụ: nếu giá trị của bạn là danh sách, ví dụ, và bạn muốn đảm bảo rằng có một danh sách trống mà bạn có thể nối vào khi chèn giá trị đầu tiên cho 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 thức 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ỉ sử dụng key_name in dict_name, thay vào đó).


2
Muốn chia sẻ rằng (sử dụng Python 2.7) thời gian chạy của một thứ tôi vừa viết, dựa rất nhiều vào các dicts, là 363.235070 bằng cách sử dụng "key in dict.keys ()" và giảm mạnh xuống chỉ còn 0,260186 bằng cách xóa phím gọi cho "(" ) "
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

@Ido_f Tôi nghi ngờ đó là một thứ khác trong chương trình của bạn là một thứ khác, nhưng thực tế không phải vậy key in dict.keys(). Hãy thử xóa tất cả mã ngoại trừ kiểm tra này và xem kết quả của bạn là gì.
Charles Addis

101

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

if 'key1' in dict:
    ...

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


100
Đây là nhiều hơn một cải tiến mỹ phẩm. Thời gian để tìm một khóa sử dụng phương thức 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

5
Chữ O (1) có vẻ không đúng lắm. Bạn có chắc chắn nó không giống như O (log n) không?
quang phổ

12
Đó là sự phức tạp của một tra cứu chính tả, trung bình là O (1) và tệ nhất là O (n). .list () sẽ luôn là O (n). wiki.python.org/moin/TimeComplexity
Leonora Tindall

1
điều này cũng tránh được sự phân bổ thêm. (quan trọng để tạo vòng lặp nhanh hơn một chút)
Nurettin

56

Để biết thêm thông tin về việc thực hiện tốc độ của các phương pháp được đề xuất của câu trả lời được chấp nhận (vòng lặp 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 inhoặc defaultdictđược đề nghị chống lại get.


6
hoàn toàn đồng ý rằng get1,84s là <1,07 * 2 ;-P
Paul Rigor

54

Tôi sẽ khuyên bạn nên sử dụng setdefaultphương pháp thay thế. 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}

9
Điều gì setdefaultphải làm với câu hỏi của OP?
hughdbrown

18
@hughdbrown "Tôi muốn kiểm tra xem 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." Đôi khi các bài đăng bao gồm mã tạo ra một loạt các phản hồi cho một cái gì đó không hoàn toàn là mục tiêu ban đầu. Để hoàn thành mục tiêu đã nêu trong câu đầu tiên, setdefault là phương pháp hiệu quả nhất, mặc dù đó không phải là sự thay thế thả xuống cho mã mẫu được đăng.
David Berger

5
Đây là câu trả lời ưu việt 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/ khăn
Niels Bom

+1 cho một câu trả lời thông tin, điều đó đã dạy tôi điều gì đó. Tuy nhiên, liệu đó có phải là giải pháp tốt nhất hay không phụ thuộc vào những gì người viết mã nghĩ đến; 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 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 ấy sẽ thêm 1 vào số đếm hiện có, trong trường hợp đó `d [key] = d.get (key, 0) + 1 'là giải pháp sạch nhất (như Chris chỉ ra, sau câu trả lời của bạn đã được viết). (Tôi chỉ bận tâm đề cập đến điều này, trong trường hợp độc giả tương lai đến đây, với các nhiệm vụ khác nhau trong tâm trí.)
ToolmakerSteve 16/12/13

1
@ToolmakerSteve Đúng. Vấn đề ở đây là câu hỏi của OP không đủ rõ ràng.
Niels Bom

45

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

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

33

Điều gì về việc sử dụng EAFP (dễ dàng yêu cầu sự tha thứ hơn là sự cho phép):

try:
   blah = dict["mykey"]
   # key exists in dict
except KeyError:
   # key doesn't exist in dict

Xem các bài viết SO khác:

Sử dụng thử vs nếu trong python hoặc

Kiểm tra sự tồn tại của thành viên trong Python


12
Thử / ngoại trừ có thể đắt hơn nếu có khả năng khóa thường không tồn tại. Từ bài đăng mà bạn đã tham khảo: "[Tôi] nếu bạn mong đợi rằng 99% kết quả thời gian thực sự sẽ chứa thứ gì đó có thể lặp lại, tôi sẽ sử dụng phương pháp thử / ngoại trừ. Sẽ nhanh hơn nếu ngoại lệ thực sự là ngoại lệ. Nếu kết quả là Không có hơn 50% thời gian, sau đó sử dụng if có lẽ tốt hơn. [...] [A] n nếu câu lệnh luôn khiến bạn phải trả giá, thì gần như miễn phí để thiết lập một khối thử / ngoại trừ. Nhưng khi Exception thực sự xảy ra, chi phí cao hơn nhiều. " stackoverflow.com/a/185844/1094092
billrichards

28

Sử dụng toán tử ternary:

message = "blah" if 'key1' in dict else "booh"
print(message)

20

Những cách mà bạn có thể nhận được kết quả là:

Cái nào tốt hơn là phụ thuộc vào 3 điều:

  1. Liệu từ điển 'thường có khóa' hay 'bình thường không có khóa'.
  2. Bạn có định sử dụng các điều kiện như nếu ... khác ... khác ... khác không?
  3. Từ điển lớn như thế nào?

Đọc thêm: http://paltman.com/try-except-performance-in-python-a-simple-test/

Sử dụng thử / chặn thay vì 'trong' hoặc 'nếu':

try:
    my_dict_of_items[key_i_want_to_check]
except KeyError:
    # Do the operation you wanted to do for "key not present in dict".
else:
    # Do the operation you wanted to do with "key present in dict."

2
Tốt nhưng cần được hiện thực hóa cho python 3. Tôi đã chuyển đổi tập lệnh của trang web 2to3và thấy rằng cú pháp không thử luôn nhanh hơn cú pháp thử, ngay cả trong trường hợp khóa nằm trong lệnh chính tả.
Jean Paul

18

Chỉ Python 2: (và inđã hỗ trợ python 2.7 )

bạn có thể sử dụng phương thức has_key ():

if dict.has_key('xyz')==1:
    #update the value for the key
else:
    pass

22
.has_key()đã bị phản đối ; bạn nên sử dụng innhư thể hiện trong các câu trả lời khác.
Brad Koch

12
BTW, tôi khuyên bạn nên đọc TẤT CẢ các câu trả lời hiện có cho câu hỏi OLD , trước khi trả lời nó. Câu trả lời này không thêm gì, vì gợi ý đã tồn tại trong câu trả lời của Michael, từ '09. (Tôi không có ý ngăn cản nỗ lực thêm thứ gì đó hữu ích vào cuộc thảo luận. Hãy tiếp tục thử.)
ToolmakerSteve 16/12/13

16

Chỉ là một FYI thêm vào Chris. B (câu trả lời hay nhất):

d = defaultdict(int)

Hoạt động tốt; Lý do là việc gọi int()trả về 0, đó là những gì diễn defaultdictra sau hậu trường (khi xây dựng một từ điển), do đó có tên "Factory Function" trong tài liệu.


2
Nếu bạn đang tạo một từ điển đếm, bạn nên sử dụng Counter (giả sử Python 2.7). Và tôi đã sử dụng defaultdict(lambda: 0)thay defaultdict(int)vì bởi vì tôi nghĩ nó rõ ràng hơn những gì đang diễn ra; người đọc không cần biết bạn nhận được 0nếu bạn gọi int()mà không có đối số. YMMV.
Chris B.

9

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

Để có được ý tưởng làm thế nào, trước tiên chúng ta kiểm tra những phương thức nào chúng ta có thể gọi trong từ điển. Dưới đây là các phương pháp:

d={'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

Python Dictionary clear()       Removes all Items
Python Dictionary copy()        Returns Shallow Copy of a Dictionary
Python Dictionary fromkeys()    Creates dictionary from given sequence
Python Dictionary get()         Returns Value of The Key
Python Dictionary items()       Returns view of dictionary (key, value) pair
Python Dictionary keys()        Returns View Object of All Keys
Python Dictionary pop()         Removes and returns element having given key
Python Dictionary popitem()     Returns & Removes Element From Dictionary
Python Dictionary setdefault()  Inserts Key With a Value if Key is not Present
Python Dictionary update()      Updates the Dictionary 
Python Dictionary values()      Returns view of all values in dictionary

Phương thức tàn bạo để kiểm tra xem khóa đã tồn tại có thể là get()phương thức:

d.get("key")

Hai phương pháp thú vị khác items()keys()có vẻ như quá nhiều công việc. Vì vậy, hãy kiểm tra nếu get()là phương pháp phù hợp với chúng tôi. Chúng tôi có lệnh của chúng tôi d:

d= {'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

In ấn cho thấy chìa khóa chúng tôi không có sẽ trở lại None:

print(d.get('key')) #None
print(d.get('clear')) #0
print(d.get('copy')) #1

Chúng tôi có thể sử dụng thông tin đó để lấy thông tin nếu khóa có hoặc không. Nhưng hãy xem xét điều này nếu chúng ta tạo ra một lệnh với một key:None:

d= {'key':None}
print(d.get('key')) #None
print(d.get('key2')) #None

Dẫn đầu get()phương pháp đó là không đáng tin cậy trong trường hợp một số giá trị có thể None. Câu chuyện này nên có một kết thúc hạnh phúc hơn. Nếu chúng ta sử dụng bộ inso sánh:

print('key' in d) #True
print('key2' in d) #False

Chúng tôi nhận được kết quả chính xác. Chúng tôi có thể kiểm tra mã byte Python:

import dis
dis.dis("'key' in d")
#   1           0 LOAD_CONST               0 ('key')
#               2 LOAD_NAME                0 (d)
#               4 COMPARE_OP               6 (in)
#               6 RETURN_VALUE

dis.dis("d.get('key2')")
#   1           0 LOAD_NAME                0 (d)
#               2 LOAD_METHOD              1 (get)
#               4 LOAD_CONST               0 ('key2')
#               6 CALL_METHOD              1
#               8 RETURN_VALUE

Điều này cho thấy intoán tử so sánh không chỉ đáng tin cậy hơn mà thậm chí còn nhanh hơn get().


.get()có thể có một đối số thứ hai cho defaultgiá trị, có thể được sử dụng để xử lý vấn đề ở đâu key:None. ví dụ: d.get("key", False)
Alex

.get()là cách nhanh nhất Một lựa chọn khác là gán trong một try/ exceptkhối
HCLivess

7

Từ điển Python có phương thức gọi là __contains__. Phương thức này sẽ trả về True nếu từ điển có khóa khác trả về Sai.

 >>> temp = {}

 >>> help(temp.__contains__)

Help on built-in function __contains__:

__contains__(key, /) method of builtins.dict instance
    True if D has a key k, else False.

2
Đó là thực tế rất xấu để gọi __contains__trực tiếp. Cách làm chính xác, là sử dụng intoán tử, đó là hàm containment checkgọi __contains__hàm.
dùng1767754

@ user1767754 Tôi đang sử dụng foo = x['foo'] if x.__contains__('foo') else 'bar'. Bất kỳ ý tưởng làm thế nào có thể sử dụng intoán tử như là một phần của biểu thức này?
donrondadon

1
foo = x['foo'] if 'foo' in x else 'bar'
Ray Wu

5

Chia sẻ thêm một cách kiểm tra nếu một khóa tồn tại bằng cách sử dụng các toán tử boolean.

d = {'a': 1, 'b':2}
keys = 'abcd'

for k in keys:
    x = (k in d and 'blah') or 'boo'
    print(x) 

Điều này trở lại

>>> blah
>>> blah
>>> boo
>>> boo

Giải trình

Trước tiên, bạn nên biết rằng trong Python, 0, None, hoặc đối tượng với zero chiều dài đánh giá để False. Mọi thứ khác đánh giá True. Các phép toán Boolean được đánh giá từ trái sang phải và trả về toán hạng không đúng hoặc sai.

Hãy xem một ví dụ:

>>> 'Some string' or 1/0 
'Some string'
>>>

Kể từ khi 'Some string'đánh giá True, phần còn lại orkhông được đánh giá và không có sự phân chia nào cho lỗi không được nêu ra.

Nhưng nếu chúng ta chuyển đổi thứ tự 1/0được đánh giá đầu tiên và đưa ra một ngoại lệ:

>>> 1/0 or 'Some string'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 

Chúng ta có thể sử dụng điều này cho mẫu để kiểm tra nếu một khóa tồn tại.

(k in d and 'blah')

làm giống như

if k in d:
    'blah'
else:
    False

Điều này đã trả về kết quả chính xác nếu khóa tồn tại, nhưng chúng tôi muốn nó in 'boo' khi không có. Vì vậy, chúng tôi lấy kết quả và ornó với'boo'

>>> False or 'boo'
'boo'
>>> 'blah' or 'boo'
'blah'
>>> 

1

Bạn có thể sử dụng forvòng lặp để lặp lại từ điển và lấy tên của khóa bạn muốn tìm trong từ điển, sau đó kiểm tra xem nó có tồn tại hay không sử dụng ifđiều kiện:

dic = {'first' : 12, 'second' : 123}
for each in dic:
    if each == 'second': 
        print('the key exists and the corresponding value can be updated in the dictionary')

kiểm tra mã bởi vì đầu ra cho cái này là it is existnot exist
system123456

Tại sao lại sử dụng từ điển nếu điều này là để thực hiện tìm kiếm tuyến tính?
Jean-François Fabre
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.