ฉันจะตรวจสอบได้อย่างไรว่ามีหลายคีย์ใน dict ในรอบเดียว?


218

ฉันต้องการทำสิ่งที่ชอบ:

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

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

ฉันจะตรวจสอบได้อย่างไรว่า 'foo' และ 'bar' อยู่ใน dict foo หรือไม่

คำตอบ:


363

คุณสามารถทำสิ่งนี้ได้:

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

10
+1, ฉันชอบสิ่งนี้ดีกว่าคำตอบของ Greg เพราะมันกระชับและเร็วกว่า (ไม่มีการสร้างรายการชั่วคราวที่ไม่เกี่ยวข้องและการใช้ประโยชน์อย่างเต็มที่จากการลัดวงจร)
Alex Martelli

4
ฉันรักทุกคน () และใด ๆ () พวกเขาทำให้อัลกอริทึมมากมายที่สะอาดกว่ามาก
30990 hughdbrown เมื่อ

ในที่สุดฉันก็ลงเอยด้วยการใช้โซลูชันนี้ มันดูดีที่สุดสำหรับชุดข้อมูลขนาดใหญ่ เมื่อตรวจสอบว่ามี 25 หรือ 30 ปุ่ม

4
เป็นวิธีแก้ปัญหาที่ดีเนื่องจากมีการลัดวงจรโดยเฉพาะหากการทดสอบล้มเหลวบ่อยกว่าไม่ เว้นแต่คุณจะสามารถสร้างชุดคีย์ที่น่าสนใจได้เพียงครั้งเดียวและตรวจสอบมันหลายครั้งซึ่งในกรณีนี้setจะดีกว่า ตามปกติ ... วัดมัน! -)
Alex Martelli

ฉันใช้สิ่งนี้ทุกครั้งที่มันดูดีกว่าวิธี "ปกติ" ด้วยทั้งหมดและหรือหรือ ... มันยังดี 'เพราะคุณสามารถใช้ "ทั้งหมด" หรือ "ใด ๆ " ... นอกจากนี้คุณสามารถมี " k ใน foo" หรือ 'k ไม่ได้อยู่ใน foo' ขึ้นอยู่กับการทดสอบที่คุณกำลังพยายามที่จะดำเนินการ
Terence Honles

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

หากคุณยังอยู่ใน Python 2 คุณสามารถทำได้

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

หากคุณยังอยู่ในPython เก่าจริงๆ <= 2.6 คุณสามารถโทรหาsetDict ได้ แต่มันจะวนซ้ำตามคำสั่งทั้งหมดเพื่อสร้างชุดและช้า:

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

ดูดี! สิ่งเดียวที่ฉันไม่ชอบคือคุณต้องสร้างชุดชั่วคราว แต่มันกะทัดรัดมาก ดังนั้นฉันจึงต้องพูดว่า ... การใช้ฉากให้เป็นประโยชน์!
เทอเรนซ์ Honles

17
ใน python 3 คุณสามารถบอกได้ว่าควรset(("foo","bar")) <= myDict.keys()หลีกเลี่ยงชุดชั่วคราวใดดังนั้นเร็วกว่ามาก สำหรับการทดสอบของฉันมันเกี่ยวกับความเร็วเดียวกับการใช้ทั้งหมดเมื่อแบบสอบถามคือ 10 รายการ มันจะช้าลงเมื่อเคียวรีใหญ่ขึ้น
John La Rooy

1
ฉันโพสต์การทดสอบของฉันไว้เป็นคำตอบ stackoverflow.com/questions/1285911/…
John La Rooy

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

11
สำหรับทุกคนที่สงสัยว่าทำไมงานนี้: ตัวดำเนินการ <= เหมือนกับวิธีการใช้. set issubset (): docs.python.org/3/library/stdtypes.html#set-types-set-frozenset
edepe

41

แท่นขุดเจาะที่เรียบง่ายสำหรับ 3 ตัวเลือก

ใส่ค่าของคุณเองสำหรับ D และ 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
งูหลาม 2.7 มีที่จะทำให้d.viewkeys() set(q) <= d.viewkeys()
Martijn Pieters

Python 2.7.5มีd.keys()วิธีการเช่นกัน
Ivan Kharlamov

3
@IvanKharlamov แต่ใน Python2 จะไม่ส่งคืนวัตถุที่เข้ากันได้กับset(q) <= ...
John La Rooy

1
ไม่ดีของฉันคุณเป็นจุดที่แน่นอน: มันกลับTypeError: can only compare to a setมา ขออภัย! :))
Ivan Kharlamov

1
สำหรับงูหลาม 2 d.viewkeys() >= set(q)สลับการสั่งซื้อ: ฉันมาที่นี่เพื่อพยายามหาสาเหตุว่าทำไมลำดับถึงสำคัญ!
Veedrac

34

คุณไม่จำเป็นต้องห่อด้านซ้ายในชุด คุณสามารถทำสิ่งนี้:

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

สิ่งนี้ยังทำงานได้ดีกว่าall(k in d...)โซลูชัน


2
สิ่งนี้ยังทำงานได้ดีกว่าโซลูชันทั้งหมด (k in d ... ) ผมแนะนำนี้เป็นแก้ไข แต่ถูกปฏิเสธในบริเวณที่มันเป็นดีกว่าที่จะเพิ่มความคิดเห็น ดังนั้นที่นี่ฉันทำเช่นนั้น
miraculixx

@miraculixx เพิ่มความคิดเห็นไม่ได้ ดีกว่าที่จะแก้ไขข้อมูลที่เกี่ยวข้องลงในคำตอบและลบความคิดเห็น
endolith

1
@endolith ฉันเห็นด้วยบางคนเห็นได้ชัดว่าไม่ได้อย่างที่คุณเห็นในการแก้ไขที่ถูกปฏิเสธที่ฉันทำตั้งแต่แรก อย่างไรก็ตามนั่นคือการอภิปรายสำหรับเมตาไม่ได้ที่นี่
miraculixx

ใครช่วยอธิบายเรื่องนี้ได้มั้ย ฉันรวบรวมว่า {} สร้างชุด แต่ตัวดำเนินการที่น้อยกว่าหรือเท่ากันทำงานที่นี่ได้อย่างไร
Locane

1
@Locane ตัวดำเนินการ <= ทดสอบว่าชุดแรกเป็นชุดย่อยของชุดที่สอง คุณสามารถทำ {'foo', 'bar'}. issubset (somedict) เอกสารสำหรับวิธีการชุดสามารถพบได้ที่นี่: docs.python.org/2/library/sets.html
Meow

24

ใช้ชุด :

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

อีกวิธีหนึ่งคือ:

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

2
set (d) ตามที่ฉันใช้ในคำตอบของฉันเหมือนกับ set (d.keys) แต่เร็วกว่าสั้นกว่าและฉันจะบอกว่าดีกว่า stylistically
Alex Martelli

set(d)เป็นเช่นเดียวกับset(d.keys())(ไม่รวมรายการกลางที่d.keys()สร้าง)
เช็น Ritzel

11

เกี่ยวกับสิ่งนี้:

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

8
แน่นอนไม่เพียง allแต่ไม่จำเป็นที่เป็นอันตรายในเชิงบวกเช่นที่พวกเขาเป็นอุปสรรคต่อพฤติกรรมการลัดวงจรปกติของ
Alex Martelli


9

ในขณะที่ฉันชอบคำตอบของ Alex Martelli ฉันไม่ได้ดูเหมือน Pythonic กับฉัน นั่นคือฉันคิดว่าส่วนสำคัญของการเป็น Pythonic ที่เข้าใจได้ง่าย ด้วยเป้าหมาย<=นั้นไม่ง่ายที่จะเข้าใจ

ในขณะที่มันเป็นตัวละครมากขึ้นการใช้issubset()ตามคำแนะนำของ Karl Voigtland นั้นเป็นที่เข้าใจได้มากกว่า เนื่องจากวิธีการนั้นสามารถใช้พจนานุกรมเป็นอาร์กิวเมนต์ได้โซลูชันสั้น ๆ ที่เข้าใจได้คือ:

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

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

ฉันต้องการใช้{'foo', 'bar'}แทนset(('foo', 'bar'))เพราะสั้นกว่า อย่างไรก็ตามมันไม่เข้าใจและฉันคิดว่าเครื่องมือจัดฟันนั้นสับสนง่ายเกินไปว่าเป็นพจนานุกรม


2
ฉันคิดว่ามันเข้าใจได้เมื่อคุณเข้าใจความหมาย
Bobort

มันเป็นในเอกสาร.issubset()เป็นคำพ้องสำหรับ ฉันคิดว่าการอยู่ในเอกสาร Python ทำให้ Pythonic เป็นค่าเริ่มต้น
ingyhere

4

ทางออกของ Alex Martelli set(queries) <= set(my_dict)เป็นรหัสที่สั้นที่สุด แต่อาจไม่ใช่วิธีที่เร็วที่สุด สมมติว่า Q = len (เคียวรี) และ D = len (my_dict)

สิ่งนี้ต้องใช้ O (Q) + O (D) เพื่อสร้างสองชุดจากนั้น (หนึ่งหวัง!) เฉพาะ O (min (Q, D)) เพื่อทำการทดสอบเซต - สมมติว่า Python ตั้งค่าการค้นหา คือ O (1) - เป็นกรณีที่เลวร้ายที่สุด (เมื่อคำตอบคือ True)

โซลูชันเครื่องกำเนิดไฟฟ้าของ hughdbrown (et al?) all(k in my_dict for k in queries)เป็นกรณีที่เลวร้ายที่สุด O (Q)

ปัจจัยที่ทำให้เกิดความซับซ้อน:
(1) การวนซ้ำในแกดเจ็ตแบบเซ็ตนั้นกระทำด้วยความเร็ว C ในขณะที่แกดเจ็ตใด ๆ ก็ตามวนไปตามไบต์
(2) ผู้โทรเข้าของแกดเจ็ตใด ๆ อาจสามารถใช้ความรู้เกี่ยวกับความน่าจะเป็นที่จะเกิดความล้มเหลวในการสั่งซื้อรายการเคียวรีตามนั้นขณะที่แกดเจ็ตที่ตั้งค่าไว้จะไม่สามารถควบคุมได้

เช่นเคยหากความเร็วเป็นสิ่งสำคัญการเปรียบเทียบภายใต้สภาพการทำงานเป็นความคิดที่ดี


1
เครื่องกำเนิดไฟฟ้าเร็วขึ้นสำหรับทุกกรณีที่ฉันลอง stackoverflow.com/questions/1285911/…
John La Rooy


1

แลมบ์ดาใช้วิธีการอย่างไร?

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

2
คำตอบนี้เป็นคำตอบเดียวที่ถูกต้องตามหน้าที่ซึ่งจะทำงานบน Python 1.5 โดยมีการเปลี่ยนแปลงอย่างง่าย (s / True / 1 /) ... แต่ไม่มีอะไรเกิดขึ้น และสิ่งที่แท้จริงจะดีกว่าเพราะตัวเลือกเริ่มต้นหาเรื่องมากกว่าจะยัดเข้าไปในด้านหน้าของลำดับอาร์ค
John Machin

1

ในกรณีที่คุณต้องการ:

  • ยังได้รับค่าสำหรับกุญแจ
  • ตรวจสอบมากกว่าหนึ่ง dictonary

แล้ว:

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

ไม่แนะนำว่านี่ไม่ใช่สิ่งที่คุณไม่ได้คิด แต่ฉันพบว่าสิ่งที่ง่ายที่สุดมักจะดีที่สุด:

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

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

Jason, () ไม่จำเป็นใน Python


3
พวกเขายังคงอาจจะเป็นรูปแบบที่ดี ... โดยที่พวกเขาฉัน C ++ - สมองกลวงสมองมักจะสงสัยว่ามันจะถูกตีความว่าเป็น 'foo ใน (foo และ' บาร์) ใน foo ถ้า "
เจเรมี Friesner

1
ฉันเข้าใจว่าพวกเขาไม่จำเป็น ฉันแค่รู้สึกว่าพวกเขาเพิ่มความชัดเจนในกรณีนี้
เจสันเบเกอร์

0

เพียงฉันใช้เวลานี้มีสองวิธีที่ง่ายต่อการเข้าใจตัวเลือกที่กำหนดทั้งหมด ดังนั้นเกณฑ์หลักของฉันคือมีโค้ดที่อ่านง่ายมากไม่ใช่โค้ดที่รวดเร็วเป็นพิเศษ เพื่อให้เข้าใจรหัสฉันต้องการให้เป็นไปได้:

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

ความจริงที่ว่า "var <= var2.keys ()" ทำงานได้เร็วขึ้นในการทดสอบด้านล่างของฉันฉันชอบอันนี้

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

ในกรณีที่พิจารณาว่ามีเพียงบางคีย์เท่านั้นที่ทำงานได้หรือไม่:

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

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

อีกตัวเลือกหนึ่งเพื่อค้นหาว่ามีเพียงบางคีย์เท่านั้นที่ตรงกับ:

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

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

0

ตัวเลือกอื่นสำหรับการตรวจสอบว่าคีย์ทั้งหมดอยู่ใน dict หรือไม่:

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

ดูเหมือนว่าจะใช้งานได้


นี่ฉลาดและฉันก็เชื่อว่ามันใช้งานไม่ได้จนกว่าฉันจะลองด้วยตัวเอง ฉันสงสัยว่า()จะได้รับการประเมินครั้งแรกและผลในซึ่งก็จะตรวจสอบว่าTrue True in okใช้งานได้จริงอย่างไร!
durden2.0

7
('สอง' และ 'หนึ่ง' และ 'ห้า') ส่งคืน 'ห้า' ดังนั้นจึงตรวจสอบเฉพาะในกรณีที่ 'ห้า' อยู่บน dict
HardQuestions
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.