ฉันควรใช้ 'has_key ()' หรือ 'in' กับ Python dicts หรือไม่


911

ฉันสงสัยว่าจะทำอย่างไรดีกว่า:

d = {'a': 1, 'b': 2}
'a' in d
True

หรือ:

d = {'a': 1, 'b': 2}
d.has_key('a')
True

คำตอบ:


1287

in แน่นอนยิ่งกว่า pythonic

ในความเป็นจริงhas_key()จะถูกลบออกในหลาม 3.x


3
นอกจากนี้ใน Python 3 หากต้องการตรวจสอบการมีอยู่ของค่าแทนที่จะใช้คีย์ให้ลอง >>> 1 ใน d.values ​​()
riza

217
Semi-gotcha หนึ่งอันที่ควรหลีกเลี่ยงคือให้แน่ใจว่าคุณทำ: "key in some_dict" แทนที่จะเป็น "key ใน some_dict.keys ()" ทั้งสองมีความหมายเทียบเท่ากัน แต่ประสิทธิภาพการทำงานที่ชาญฉลาดช้ากว่ามาก (O (n) vs O (1)) ฉันเห็นคนทำ "ใน dict.keys ()" คิดว่ามันชัดเจนมากขึ้นและดีขึ้นกว่าเดิม
Adam Parkin

2
@ AdamParkin ฉันแสดงความคิดเห็นของคุณในคำตอบของฉันstackoverflow.com/a/41390975/117471
Bruno Bronosky

8
@ AdamParkin ใน Python 3 keys()เป็นเพียงมุมมองแบบ set-like ลงในพจนานุกรมแทนที่จะเป็นสำเนาดังนั้นx in d.keys()O (1) ยังคงx in dเป็น Pythonic มากกว่า
Arthur Tacca

2
@ AdamParkin ที่น่าสนใจฉันไม่เห็นว่า ฉันคิดว่ามันเป็นเพราะx in d.keys()ต้องสร้างและทำลายวัตถุชั่วคราวพร้อมกับการจัดสรรหน่วยความจำที่เกี่ยวข้องซึ่งx in d.keys()เป็นเพียงการดำเนินการทางคณิตศาสตร์ (คำนวณแฮช) และทำการค้นหา โปรดทราบว่าd.keys()มีเพียงประมาณ 10 เท่าเท่านั้นซึ่งยังไม่นานจริง ๆ ฉันยังไม่ได้ตรวจสอบ แต่ฉันก็ยังค่อนข้างแน่ใจว่ามันเป็นเพียง O (1)
Arthur Tacca

253

in ชนะมือลงไม่ใช่แค่ในความสง่างาม (และไม่ถูกคัดค้าน ;-) แต่รวมถึงประสิทธิภาพเช่น:

$ python -mtimeit -s'd=dict.fromkeys(range(99))' '12 in d'
10000000 loops, best of 3: 0.0983 usec per loop
$ python -mtimeit -s'd=dict.fromkeys(range(99))' 'd.has_key(12)'
1000000 loops, best of 3: 0.21 usec per loop

ในขณะที่การสังเกตต่อไปนี้ไม่เป็นความจริงเสมอไปคุณจะสังเกตได้ว่าโดยปกติแล้วใน Python โซลูชันที่เร็วกว่านั้นจะดูดีและ Pythonic นั่นเป็นเหตุผลว่าทำไมจึง-mtimeitมีประโยชน์ - ไม่ใช่แค่เกี่ยวกับการบันทึกหนึ่งร้อยนาโนวินาทีที่นี่และที่นั่น! -)


4
ขอบคุณสำหรับสิ่งนี้ทำให้การตรวจสอบว่า "ใน some_dict" นั้นเป็นเรื่องจริง O (1) ง่ายกว่ามาก (ลองเพิ่ม 99 ถึง 1999 ถึง 1999 และคุณจะพบว่า runtime มีค่าเท่ากัน)
Adam Parkin

2
has_keyดูเหมือนจะเป็น O (1) ด้วย
dan-gph


42

ใช้dict.has_key()ถ้า (และเฉพาะในกรณี) รหัสของคุณจะต้องสามารถรันได้โดย Python เวอร์ชันที่เก่ากว่า 2.3 (เมื่อkey in dictถูกนำมาใช้)


1
การอัพเดต WebSphere ในปี 2013 ใช้ Jython 2.1 เป็นภาษาสคริปต์หลัก น่าเสียดายที่มันยังมีประโยชน์ที่ควรทราบอีกห้าปีหลังจากที่คุณจดบันทึกไว้
ArtOfWarfare

23

มีตัวอย่างหนึ่งที่inฆ่าประสิทธิภาพได้จริง

ถ้าคุณใช้inในโอ (1) ภาชนะที่มีเพียงการดำเนินการ__getitem__และhas_key()แต่ไม่__contains__ว่าคุณจะเปิด O (1) ค้นหาเป็น O (N) ค้นหา (ตามที่inอยู่กลับไปยังการค้นหาเชิงเส้นผ่าน__getitem__)

เห็นได้ชัดว่าการแก้ไขเล็กน้อย:

def __contains__(self, x):
    return self.has_key(x)

6
คำตอบนี้ใช้ได้เมื่อมีการโพสต์ แต่ 99.95% ของผู้อ่านสามารถเพิกเฉยได้อย่างปลอดภัย ในกรณีส่วนใหญ่หากคุณกำลังทำงานกับสิ่งที่คลุมเครือนี้คุณจะรู้
wizzwizz4

2
นี่ไม่ใช่ปัญหาจริงๆ has_key()เป็นที่เฉพาะเจาะจงกับงูหลาม 2 พจนานุกรม in/ __contains__เป็น API ที่ถูกต้องที่จะใช้; สำหรับคอนเทนเนอร์ที่ไม่สามารถหลีกเลี่ยงการสแกนเต็มรูปแบบจะไม่มีhas_key()วิธีการใด ๆอยู่แล้วและหากมีวิธี O (1) วิธีนั้นจะใช้เฉพาะกรณีและขึ้นอยู่กับผู้พัฒนาที่จะเลือกประเภทข้อมูลที่เหมาะสมสำหรับปัญหา
Martijn Pieters

15

has_keyเป็นวิธีพจนานุกรม แต่inจะทำงานกับคอลเลกชันใด ๆ และแม้__contains__จะหายไปinจะใช้วิธีอื่น ๆ เพื่อทำซ้ำคอลเลกชันเพื่อค้นหา


1
และใช้กับตัววนซ้ำ "x in xrange (90, 200) <=> 90 <= x <200"
u0b34a0f6ae

1
…: นี่เป็นความคิดที่ไม่ดีมาก: 50 การดำเนินงานแทน 2
Clément

1
@ Clémentใน Python 3 มันค่อนข้างมีประสิทธิภาพในการทำการinทดสอบกับrangeวัตถุ ผมไม่แน่ใจว่าเกี่ยวกับประสิทธิภาพในการใช้ในหลาม 2 xrangeแม้ว่า ;)
PM 2Ring

@ ไม่ได้อยู่ใน Python 3; __contains__สามารถคำนวณได้เล็กน้อยว่าค่าอยู่ในช่วงหรือไม่
Martijn Pieters

1
@AlexandreHuat ช่วงเวลาของคุณมีค่าใช้จ่ายในการสร้างrangeอินสแตนซ์ใหม่ทุกครั้ง การใช้อินสแตนซ์ที่มีอยู่ก่อนหน้าเดียวการทดสอบ "จำนวนเต็มในช่วง" จะเร็วขึ้นประมาณ 40% ในการกำหนดเวลาของฉัน
MisterMiyagi

14

โซลูชัน dict.has_key () เลิกใช้แล้วใช้ 'ใน' - โปรแกรมแก้ไขข้อความประเสริฐ 3

ที่นี่ฉันได้นำตัวอย่างของพจนานุกรมชื่อ 'วัย' -

ages = {}

# Add a couple of names to the dictionary
ages['Sue'] = 23

ages['Peter'] = 19

ages['Andrew'] = 78

ages['Karren'] = 45

# use of 'in' in if condition instead of function_name.has_key(key-name).
if 'Sue' in ages:

    print "Sue is in the dictionary. She is", ages['Sue'], "years old"

else:

    print "Sue is not in the dictionary"

6
ถูกต้อง แต่ได้รับคำตอบแล้วยินดีต้อนรับสู่ Stackoveflow ขอบคุณสำหรับตัวอย่างโปรดตรวจสอบคำตอบเสมอ!
igorgue

@igorgue ฉันไม่แน่ใจเกี่ยวกับ downvotes กับเธอ คำตอบของเธออาจคล้ายกับคำตอบที่ได้ตอบไปแล้ว แต่เธอก็ยกตัวอย่าง ไม่คุ้มค่าพอที่จะเป็นคำตอบของ SO?
Akshat Agarwal

14

ขยายการทดสอบประสิทธิภาพของ Alex Martelli ด้วยความคิดเห็นของ Adam Parkin ...

$ python3.5 -mtimeit -s'd=dict.fromkeys(range( 99))' 'd.has_key(12)'
Traceback (most recent call last):
  File "/usr/local/Cellar/python3/3.5.2_3/Frameworks/Python.framework/Versions/3.5/lib/python3.5/timeit.py", line 301, in main
    x = t.timeit(number)
  File "/usr/local/Cellar/python3/3.5.2_3/Frameworks/Python.framework/Versions/3.5/lib/python3.5/timeit.py", line 178, in timeit
    timing = self.inner(it, self.timer)
  File "<timeit-src>", line 6, in inner
    d.has_key(12)
AttributeError: 'dict' object has no attribute 'has_key'

$ python2.7 -mtimeit -s'd=dict.fromkeys(range(  99))' 'd.has_key(12)'
10000000 loops, best of 3: 0.0872 usec per loop

$ python2.7 -mtimeit -s'd=dict.fromkeys(range(1999))' 'd.has_key(12)'
10000000 loops, best of 3: 0.0858 usec per loop

$ python3.5 -mtimeit -s'd=dict.fromkeys(range(  99))' '12 in d'
10000000 loops, best of 3: 0.031 usec per loop

$ python3.5 -mtimeit -s'd=dict.fromkeys(range(1999))' '12 in d'
10000000 loops, best of 3: 0.033 usec per loop

$ python3.5 -mtimeit -s'd=dict.fromkeys(range(  99))' '12 in d.keys()'
10000000 loops, best of 3: 0.115 usec per loop

$ python3.5 -mtimeit -s'd=dict.fromkeys(range(1999))' '12 in d.keys()'
10000000 loops, best of 3: 0.117 usec per loop

สถิติที่ยอดเยี่ยมบางครั้งโดยนัยอาจจะดีกว่าชัดเจน (อย่างน้อยก็ในประสิทธิภาพ) ...
varun

ขอบคุณ @varun ฉันลืมคำตอบนี้ไปแล้ว ฉันต้องทำการทดสอบชนิดนี้บ่อยขึ้น ฉันอ่านกระทู้ยาว ๆ เป็นประจำซึ่งผู้คนโต้เถียงกันเกี่ยวกับThe Best Way ™เพื่อทำสิ่งต่างๆ แต่ผมไม่ค่อยจำวิธีนี้ง่ายคือการได้รับการพิสูจน์
Bruno Bronosky

0

หากคุณมีสิ่งนี้:

t.has_key(ew)

เปลี่ยนเป็นด้านล่างเพื่อใช้งานบน Python 3.X ขึ้นไป:

key = ew
if key not in t

6
ไม่คุณกลับด้านการทดสอบ t.has_key(ew)ส่งคืนTrueถ้าการewอ้างอิงค่าเป็นกุญแจสำคัญในพจนานุกรมด้วย key not in tส่งคืนTrueถ้าค่าไม่ได้อยู่ในพจนานุกรม นอกจากนี้key = ewนามแฝงนั้นซ้ำซ้อนมาก if ew in tการสะกดคำที่ถูกต้องคือ ซึ่งเป็นสิ่งที่คำตอบที่ยอมรับจาก 8 ปีก่อนบอกคุณแล้ว
Martijn Pieters
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.