Làm cách nào để kiểm tra xem nhiều khóa có trong một lệnh trong một lần chạy không?


217

Tôi muốn làm một cái gì đó như:

foo = {'foo':1,'zip':2,'zam':3,'bar':4}

if ("foo","bar") in foo:
    #do stuff

Làm cách nào để kiểm tra xem cả 'foo' và 'bar' có trong dict foo không?

Câu trả lời:


362

Vâng, bạn có thể làm điều này:

>>> if all (k in foo for k in ("foo","bar")):
...     print "They're there!"
...
They're there!

10
+1, tôi thích điều này tốt hơn câu trả lời của Greg vì nó ngắn gọn VÀ nhanh hơn (không xây dựng danh sách tạm thời không liên quan và khai thác toàn bộ ngắn mạch).
Alex Martelli

4
Tôi yêu tất cả () và bất kỳ (). Họ làm cho rất nhiều thuật toán sạch hơn rất nhiều.
hughdbrown

Cuối cùng tôi đã kết thúc bằng cách sử dụng giải pháp này. Nó dường như là tốt nhất cho các bộ dữ liệu lớn hơn. Khi kiểm tra cho biết 25 hoặc 30 phím.

4
Đó là một giải pháp tốt nhờ ngắn mạch, đặc biệt là nếu thử nghiệm thất bại thường xuyên hơn không; trừ khi bạn có thể tạo bộ khóa quan tâm chỉ một lần và kiểm tra nó nhiều lần, trong trường hợp đó setlà ưu việt. Như thường lệ ... hãy đo nó! -)
Alex Martelli

Tôi sử dụng nó bất cứ khi nào nó trông đẹp hơn so với cách "thông thường", với tất cả và và hoặc ... nó cũng tốt 'vì bạn có thể sử dụng "tất cả" hoặc "bất kỳ" ... ngoài ra bạn có thể có " k in foo "hoặc" k not in foo "tùy thuộc vào bài kiểm tra bạn đang cố gắng thực hiện
Terence Honles

123
if {"foo", "bar"} <= myDict.keys(): ...

Nếu bạn vẫn dùng Python 2, bạn có thể làm

if {"foo", "bar"} <= myDict.viewkeys(): ...

Nếu bạn vẫn còn trên một Python thực sự cũ <= 2.6, bạn có thể gọi setdict, nhưng nó sẽ lặp lại toàn bộ lệnh để xây dựng tập hợp và điều đó rất chậm:

if set(("foo", "bar")) <= set(myDict): ...

có vẻ tốt Điều duy nhất tôi không thích là bạn phải tạo các bộ tạm thời, nhưng nó rất nhỏ gọn. Vì vậy, tôi phải nói rằng ... sử dụng tốt đẹp của bộ!
Terence Honles

17
Trong python 3 bạn có thể nói set(("foo","bar")) <= myDict.keys()cái nào tránh được bộ tạm thời, vì vậy nhanh hơn nhiều. Đối với thử nghiệm của tôi, nó có cùng tốc độ với việc sử dụng tất cả khi truy vấn là 10 mục. Nó trở nên chậm hơn khi truy vấn trở nên lớn hơn mặc dù.
John La Rooy

1
Tôi đã đăng một số bài kiểm tra của mình như một câu trả lời. stackoverflow.com/questions/1285911/
John La Rooy

30
if {'foo', 'bar'} <= set(myDict): ...
Boris Raicheff

11
Đối với bất cứ ai tự hỏi tại sao các công trình này: các nhà điều hành <= cũng giống như sử dụng .set issubset () phương pháp: docs.python.org/3/library/stdtypes.html#set-types-set-frozenset
edepe

41

Giàn khoan điểm chuẩn đơn giản cho 3 trong số các lựa chọn thay thế.

Đặt các giá trị của riêng bạn cho D và Q


>>> from timeit import Timer
>>> setup='''from random import randint as R;d=dict((str(R(0,1000000)),R(0,1000000)) for i in range(D));q=dict((str(R(0,1000000)),R(0,1000000)) for i in range(Q));print("looking for %s items in %s"%(len(q),len(d)))'''

>>> Timer('set(q) <= set(d)','D=1000000;Q=100;'+setup).timeit(1)
looking for 100 items in 632499
0.28672504425048828

#This one only works for Python3
>>> Timer('set(q) <= d.keys()','D=1000000;Q=100;'+setup).timeit(1)
looking for 100 items in 632084
2.5987625122070312e-05

>>> Timer('all(k in d for k in q)','D=1000000;Q=100;'+setup).timeit(1)
looking for 100 items in 632219
1.1920928955078125e-05

4
Python 2.7 d.viewkeys()phải thực hiện set(q) <= d.viewkeys().
Martijn Pieters

Python 2.7.5d.keys()phương pháp quá.
Ivan Kharlamov

3
@IvanKharlamov, nhưng trong Python2, nó không trả về một đối tượng tương thích vớiset(q) <= ...
John La Rooy

1
Xấu của tôi, bạn hoàn toàn phát hiện ra: nó trở lại TypeError: can only compare to a set. Lấy làm tiếc! :))
Ivan Kharlamov

1
Đối với Python 2 chuyển thứ tự : d.viewkeys() >= set(q). Tôi đến đây để cố gắng tìm hiểu tại sao đơn hàng lại quan trọng!
Veedrac

34

Bạn không cần phải bọc bên trái trong một bộ. Bạn chỉ có thể làm điều này:

if {'foo', 'bar'} <= set(some_dict):
    pass

Điều này cũng thực hiện tốt hơn so với all(k in d...)giải pháp.


2
Điều này cũng thực hiện tốt hơn so với giải pháp tất cả (k in d ...). Tôi đề nghị đây là một chỉnh sửa, nhưng nó đã bị từ chối với lý do tốt hơn là thêm một bình luận . Vì vậy, đây là tôi làm điều đó
miraculixx

@miraculixx Không tốt hơn để thêm một bình luận. Tốt hơn hết là chỉnh sửa thông tin liên quan thành câu trả lời và xóa các bình luận.
endolith

1
@endolith Tôi đồng ý, một số người rõ ràng là không như bạn có thể thấy trong bản chỉnh sửa bị từ chối mà tôi đã làm ở nơi đầu tiên. Dù sao đó là một cuộc thảo luận cho meta không dành cho ở đây.
miraculixx

Ai đó có thể giải thích điều này xin vui lòng? Tôi đã tập hợp rằng {} tạo ra một tập hợp, nhưng làm thế nào là toán tử nhỏ hơn hoặc bằng nhau làm việc ở đây?
Locane

1
@Locane Toán tử <= kiểm tra nếu tập đầu tiên là tập con của tập thứ hai. Bạn cũng có thể thực hiện {'foo', 'bar'}. Ngay bây giờ (somedict). Tài liệu về phương pháp thiết lập có thể được tìm thấy ở đây: docs.python.org/2/l Library / sets.html
Meow

24

Sử dụng bộ :

if set(("foo", "bar")).issubset(foo):
    #do stuff

Cách khác:

if set(("foo", "bar")) <= set(foo):
    #do stuff

2
set (d) như tôi đã sử dụng trong câu trả lời của mình cũng giống như set (d.keys ()) nhưng nhanh hơn, ngắn hơn và tôi sẽ nói là thích hợp hơn về mặt phong cách.
Alex Martelli

set(d)giống như set(d.keys())(không có danh sách trung gian d.keys()xây dựng)
Jochen Ritzel

11

Còn cái này thì sao:

if all([key in foo for key in ["foo","bar"]]):
    # do stuff
    pass

8
thật vậy, không chỉ không cần thiết, có hại tích cực, vì chúng cản trở hành vi ngắn mạch thông thường của all.
Alex Martelli

10

Tôi nghĩ rằng đây là thông minh nhất và pithonic.

{'key1','key2'} <= my_dict.keys()

9

Mặc dù tôi thích câu trả lời của Alex Martelli, nhưng nó không giống Pythonic đối với tôi. Đó là, tôi nghĩ rằng một phần quan trọng của việc trở thành Pythonic là dễ hiểu. Với mục tiêu đó, <=không dễ hiểu.

Mặc dù đó là nhiều ký tự hơn, sử dụng issubset()theo gợi ý của câu trả lời của Karl Voigtland là dễ hiểu hơn. Vì phương pháp đó có thể sử dụng từ điển làm đối số, nên một giải pháp ngắn gọn, dễ hiểu là:

foo = {'foo': 1, 'zip': 2, 'zam': 3, 'bar': 4}

if set(('foo', 'bar')).issubset(foo):
    #do stuff

Tôi muốn sử dụng {'foo', 'bar'}thay thế set(('foo', 'bar')), vì nó ngắn hơn. Tuy nhiên, điều đó không dễ hiểu và tôi nghĩ rằng niềng răng quá dễ bị nhầm lẫn là một cuốn từ điển.


2
Tôi nghĩ nó có thể hiểu được một khi bạn hiểu ý nghĩa của nó.
Bobort

nằm trong tài liệu như một từ đồng nghĩa với .issubset(). Tôi nghĩ rằng trong tài liệu Python làm cho nó Pythonic theo mặc định.
trong

4

Giải pháp của Alex Martelli set(queries) <= set(my_dict)là mã ngắn nhất nhưng có thể không phải là nhanh nhất. Giả sử Q = len (truy vấn) và D = len (my_dict).

Điều này cần O (Q) + O (D) để tạo hai bộ và sau đó (một hy vọng!) Chỉ O (min (Q, D)) để thực hiện kiểm tra tập hợp con - tất nhiên giả sử rằng Python thiết lập tra cứu là O (1) - đây là trường hợp xấu nhất (khi câu trả lời là True).

Giải pháp máy phát của hughdbrown (et al?) all(k in my_dict for k in queries)Là trường hợp xấu nhất O (Q).

Các yếu tố phức tạp:
(1) các vòng lặp trong tiện ích dựa trên tập hợp đều được thực hiện ở tốc độ C trong khi tiện ích dựa trên bất kỳ đang lặp lại qua mã byte.
(2) Người gọi của tiện ích dựa trên bất kỳ có thể có thể sử dụng bất kỳ kiến ​​thức nào về xác suất thất bại để đặt hàng các mục truy vấn phù hợp trong khi tiện ích dựa trên tập hợp không cho phép điều khiển như vậy.

Như mọi khi, nếu tốc độ là quan trọng, điểm chuẩn trong điều kiện hoạt động là một ý tưởng tốt.


1
Máy phát điện nhanh hơn cho tất cả các trường hợp tôi đã thử. stackoverflow.com/questions/1285911/
John La Rooy

2

Bạn có thể sử dụng .issubset () cũng

>>> {"key1", "key2"}.issubset({"key1":1, "key2":2, "key3": 3})
True
>>> {"key4", "key2"}.issubset({"key1":1, "key2":2, "key3": 3})
False
>>>

1

Làm thế nào về việc sử dụng lambda?

 if reduce( (lambda x, y: x and foo.has_key(y) ), [ True, "foo", "bar"] ): # do stuff

2
Câu trả lời này là câu trả lời đúng chức năng duy nhất sẽ hoạt động trên Python 1.5 với một thay đổi đơn giản (s / True / 1 /) ... nhưng không có gì khác cho nó. VÀ điều đúng sẽ tốt hơn khi trình khởi tạo tùy chọn lập luận thay vì nhồi nhét vào phía trước của chuỗi arg.
John Machin

1

Trong trường hợp bạn muốn:

  • cũng nhận được các giá trị cho các phím
  • kiểm tra nhiều hơn một dictonary

sau đó:

from operator import itemgetter
foo = {'foo':1,'zip':2,'zam':3,'bar':4}
keys = ("foo","bar") 
getter = itemgetter(*keys) # returns all values
try:
    values = getter(foo)
except KeyError:
    # not both keys exist
    pass

1

Không gợi ý rằng đây không phải là điều bạn chưa từng nghĩ tới, nhưng tôi thấy rằng điều đơn giản nhất thường là tốt nhất:

if ("foo" in foo) and ("bar" in foo):
    # do stuff

1
>>> if 'foo' in foo and 'bar' in foo:
...     print 'yes'
... 
yes

Jason, () không cần thiết trong Python.


3
Tuy nhiên, chúng có thể là phong cách tốt ... không có chúng, bộ não được bổ sung C ++ của tôi luôn tự hỏi liệu nó có được hiểu là "nếu 'foo in (foo và' bar ') trong foo:"
Jeremy Friesner

1
Tôi hiểu rằng họ không cần thiết. Tôi chỉ cảm thấy rằng họ thêm rõ ràng trong trường hợp này.
Jason Baker

0

Chỉ cần tôi thực hiện điều này, có hai phương pháp dễ hiểu về tất cả các tùy chọn đã cho. Vì vậy, tiêu chí chính của tôi là có mã rất dễ đọc, không phải mã đặc biệt nhanh. Để giữ mã dễ hiểu, tôi thích các khả năng nhất định:

  • var <= var2.keys ()
  • var.issubset (var2)

Thực tế là "var <= var2.keys ()" thực thi nhanh hơn trong thử nghiệm của tôi dưới đây, tôi thích cái này hơn.

import timeit

timeit.timeit('var <= var2.keys()', setup='var={"managed_ip", "hostname", "fqdn"}; var2= {"zone": "test-domain1.var23.com", "hostname": "bakje", "api_client_ip": "127.0.0.1", "request_data": "", "request_method": "GET", "request_url": "hvar2p://127.0.0.1:5000/test-domain1.var23.com/bakje", "utc_datetime": "04-Apr-2019 07:01:10", "fqdn": "bakje.test-domain1.var23.com"}; var={"managed_ip", "hostname", "fqdn"}')
0.1745898080000643

timeit.timeit('var.issubset(var2)', setup='var={"managed_ip", "hostname", "fqdn"}; var2= {"zone": "test-domain1.var23.com", "hostname": "bakje", "api_client_ip": "127.0.0.1", "request_data": "", "request_method": "GET", "request_url": "hvar2p://127.0.0.1:5000/test-domain1.var23.com/bakje", "utc_datetime": "04-Apr-2019 07:01:10", "fqdn": "bakje.test-domain1.var23.com"}; var={"managed_ip", "hostname", "fqdn"};')
0.2644960229999924

0

Trong trường hợp xác định liệu chỉ một số khóa khớp với nhau, điều này hoạt động:

any_keys_i_seek = ["key1", "key2", "key3"]

if set(my_dict).intersection(any_keys_i_seek):
    # code_here
    pass

Một tùy chọn khác để tìm nếu chỉ một số phím khớp:

any_keys_i_seek = ["key1", "key2", "key3"]

if any_keys_i_seek & my_dict.keys():
    # code_here
    pass

0

Một tùy chọn khác để phát hiện xem tất cả các khóa có trong một lệnh không:

dict_to_test = { ... }  # dict
keys_sought = { "key_sought_1", "key_sought_2", "key_sought_3" }  # set

if keys_sought & dict_to_test.keys() == keys_sought: 
    # yes -- dict_to_test contains all keys in keys_sought
    # code_here
    pass

-4
>>> ok
{'five': '5', 'two': '2', 'one': '1'}

>>> if ('two' and 'one' and 'five') in ok:
...   print "cool"
... 
cool

Cái này có vẻ hiệu quả


Điều này thật thông minh và tôi đã tin rằng nó không hoạt động cho đến khi tôi tự thử. Tôi nghi ngờ ()sẽ được đánh giá đầu tiên và kết quả True, sau đó sẽ kiểm tra xem True in ok. Làm thế nào điều này thực sự hoạt động?!
durden2.0

7
('hai' và 'một' và 'năm') trả về 'năm', do đó, nó thực sự chỉ kiểm tra nếu 'năm' nằm trong lệnh
HardQuestions
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.