Python ถ้าไม่ใช่ == vs if! =


183

อะไรคือความแตกต่างระหว่างโค้ดสองบรรทัดนี้:

if not x == 'val':

และ

if x != 'val':

หนึ่งมีประสิทธิภาพมากกว่าอื่น ๆ

มันจะดีกว่าที่จะใช้

if x == 'val':
    pass
else:

101
ดีกว่าคือสิ่งที่คุณสามารถอ่านได้ฉันสงสัยว่าคอขวดของโปรแกรมของคุณจะอยู่ที่นี่
Thomas Ayoub

1
คำถามนี้ทำให้ฉันสนใจในกรณี "x not in list" และ "not x in list"
SomethingSomething

5
@Something บางสิ่งที่พวกเขาตีความเหมือนกัน
jonrsharpe

4
@Something การอ้างอิงบางอย่างสำหรับความคิดเห็นด้านบนของฉัน: stackoverflow.com/q/8738388/3001761
jonrsharpe

1
@Something บางสิ่งก็เหมือนกันสำหรับพวกนั้นเช่นกัน; มันเป็นวิธีการตีความไวยากรณ์มันไม่สำคัญว่าตัวถูกดำเนินการทั้งสองเป็นอย่างไร
jonrsharpe

คำตอบ:


229

การใช้disเพื่อดู bytecode ที่สร้างขึ้นสำหรับสองเวอร์ชัน:

not ==

  4           0 LOAD_FAST                0 (foo)
              3 LOAD_FAST                1 (bar)
              6 COMPARE_OP               2 (==)
              9 UNARY_NOT           
             10 RETURN_VALUE   

!=

  4           0 LOAD_FAST                0 (foo)
              3 LOAD_FAST                1 (bar)
              6 COMPARE_OP               3 (!=)
              9 RETURN_VALUE   

หลังมีการดำเนินงานน้อยลงและดังนั้นจึงมีแนวโน้มที่จะมีประสิทธิภาพมากขึ้นเล็กน้อย


มันชี้ให้เห็นในคอมมิชชัน (ขอบคุณ@Quincunx ) ว่าที่คุณif foo != barเทียบกับif not foo == barจำนวนของการดำเนินการเหมือนกันทุกอย่างมันเป็นเพียงการCOMPARE_OPเปลี่ยนแปลงและPOP_JUMP_IF_TRUEสลับไปที่POP_JUMP_IF_FALSE:

not ==:

  2           0 LOAD_FAST                0 (foo)
              3 LOAD_FAST                1 (bar)
              6 COMPARE_OP               2 (==)
              9 POP_JUMP_IF_TRUE        16

!=

  2           0 LOAD_FAST                0 (foo)
              3 LOAD_FAST                1 (bar)
              6 COMPARE_OP               3 (!=)
              9 POP_JUMP_IF_FALSE       16

ในกรณีนี้เว้นแต่มีความแตกต่างในปริมาณงานที่ต้องใช้สำหรับการเปรียบเทียบแต่ละครั้งไม่น่าที่คุณจะเห็นความแตกต่างด้านประสิทธิภาพเลย


อย่างไรก็ตามโปรดทราบว่าทั้งสองเวอร์ชันจะไม่เหมือนกันในเชิงตรรกะเสมอไปเนื่องจากจะขึ้นอยู่กับการใช้งาน__eq__และ__ne__สำหรับวัตถุที่เป็นปัญหา ตามเอกสารข้อมูลโมเดล :

ไม่มีความสัมพันธ์โดยนัยระหว่างโอเปอเรเตอร์การเปรียบเทียบ ความจริงx==yไม่ได้หมายความว่าx!=yเป็นเรื่องผิด

ตัวอย่างเช่น:

>>> class Dummy(object):
    def __eq__(self, other):
        return True
    def __ne__(self, other):
        return True


>>> not Dummy() == Dummy()
False
>>> Dummy() != Dummy()
True

ในที่สุดและบางทีสิ่งที่สำคัญที่สุด: โดยทั่วไปที่ทั้งสองมีเหตุผลเหมือนกันคืออ่านได้มากขึ้นกว่าx != ynot x == y


29
ในทางปฏิบัติคลาสใด ๆ ที่__eq__ไม่สอดคล้องกับ__ne__นั้นจะถูกแบน
Kevin

8
โปรดทราบว่ามันไม่เป็นความจริงเสมอไปที่not x == yมีคำแนะนำอีกหนึ่ง เมื่อฉันใส่รหัสลงในifมันกลับกลายเป็นว่าพวกเขาทั้งสองมีจำนวนคำสั่งเท่ากันมีเพียงคำสั่งเดียวPOP_JUMP_IF_TRUEและอีกอันหนึ่งPOP_JUMP_IF_FALSE(นั่นเป็นข้อแตกต่างระหว่างพวกเขาCOMPARE_OPเท่านั้น เมื่อฉันรวบรวมรหัสโดยไม่ต้องifs ฉันได้สิ่งที่คุณได้รับ
Justin

1
อีกตัวอย่างหนึ่งที่==และ!=ไม่ได้เกิดร่วมกันเป็นพิเศษคือการใช้งานแบบ SQL ที่เกี่ยวข้องกับnullค่า ใน SQL nullจะไม่กลับtrueไป!=เปรียบเทียบกับค่าอื่น ๆ ดังนั้นการใช้ python ของอินเตอร์เฟส SQL อาจมีปัญหาเดียวกัน
Joe

ฉันเริ่มหวังว่าฉันจะไม่ได้พูดถึงความแตกต่างที่เป็นไปได้ระหว่างnot ==และ!=ดูเหมือนว่าจะเป็นส่วนที่น่าสนใจที่สุดของคำตอบของฉัน! ฉันไม่คิดว่านี่เป็นสถานที่ที่จะอยู่ทำไมและเมื่อใดที่เหมาะสม - ดูเช่นทำไม Python จึงมี__ne__วิธีการใช้งานแทนเพียงแค่__eq__?
jonrsharpe

29

@jonrsharpe มีคำอธิบายที่ยอดเยี่ยมเกี่ยวกับสิ่งที่เกิดขึ้น ฉันคิดว่าฉันจะแสดงความแตกต่างในเวลาเมื่อเรียกใช้ตัวเลือกทั้ง 3 ตัวเลือก 10,000,000 ครั้ง (เพียงพอสำหรับความแตกต่างเล็กน้อยในการแสดง)

รหัสที่ใช้:

def a(x):
    if x != 'val':
        pass


def b(x):
    if not x == 'val':
        pass


def c(x):
    if x == 'val':
        pass
    else:
        pass


x = 1
for i in range(10000000):
    a(x)
    b(x)
    c(x)

และผลลัพธ์ของโปรไฟล์ cProfile:

ป้อนคำอธิบายรูปภาพที่นี่

ดังนั้นเราจะเห็นว่ามีความแตกต่างมากนาทีของ ~ 0.7% ระหว่างและif not x == 'val': if x != 'val':สิ่งเหล่านี้if x != 'val':เป็นวิธีที่เร็วที่สุด

อย่างไรก็ตามสิ่งที่น่าประหลาดใจที่สุดคือเราสามารถเห็นได้ว่า

if x == 'val':
        pass
    else:

ในความเป็นจริงเร็วที่สุดและเต้นif x != 'val':ประมาณ 0.3% นี่ไม่สามารถอ่านได้มาก แต่ฉันเดาว่าถ้าคุณต้องการการปรับปรุงประสิทธิภาพที่ไม่สำคัญใคร ๆ ก็สามารถลงเส้นทางนี้ได้


31
ฉันหวังว่าทุกคนจะไม่ทำตามข้อมูลนี้! การเปลี่ยนแปลงที่ไม่สามารถอ่านได้สำหรับการปรับปรุง 0.3% - หรือแม้แต่การปรับปรุง 10% - ไม่ค่อยเป็นความคิดที่ดีและการปรับปรุงประเภทนี้มีแนวโน้มที่จะหายไป (และไม่ใช่ในทางที่ดี : การเปลี่ยนแปลงเล็กน้อยในรันไทม์ Python สามารถกำจัดหรือแม้แต่คืนกำไรใด ๆ
Malvolio

1
@Malvolio นอกจากนี้ยังมีการใช้งานของ Python ที่แตกต่างกัน
Cees Timmerman

6

ใน Python แรกจะต้องดำเนินการมากกว่าหนึ่งอย่างที่จำเป็น (แทนที่จะเพียงแค่ตรวจสอบไม่เท่ากับมันต้องตรวจสอบว่ามันไม่เป็นความจริงว่ามันเท่ากันดังนั้นจึงเป็นการดำเนินการอีกหนึ่งครั้ง) มันจะเป็นไปไม่ได้ที่จะบอกความแตกต่างจากการประหารชีวิตครั้งเดียว แต่ถ้าวิ่งไปหลายครั้งสิ่งที่สองจะมีประสิทธิภาพมากกว่า โดยรวมแล้วฉันจะใช้อันที่สอง แต่ในทางคณิตศาสตร์พวกมันเหมือนกัน


5
>>> from dis import dis
>>> dis(compile('not 10 == 20', '', 'exec'))
  1           0 LOAD_CONST               0 (10)
              3 LOAD_CONST               1 (20)
              6 COMPARE_OP               2 (==)
              9 UNARY_NOT
             10 POP_TOP
             11 LOAD_CONST               2 (None)
             14 RETURN_VALUE
>>> dis(compile('10 != 20', '', 'exec'))
  1           0 LOAD_CONST               0 (10)
              3 LOAD_CONST               1 (20)
              6 COMPARE_OP               3 (!=)
              9 POP_TOP
             10 LOAD_CONST               2 (None)
             13 RETURN_VALUE

ที่นี่คุณจะเห็นว่าnot x == yมีคำแนะนำมากกว่าx != yหนึ่ง ดังนั้นความแตกต่างด้านประสิทธิภาพจะเล็กมากในกรณีส่วนใหญ่เว้นแต่คุณจะทำการเปรียบเทียบหลายล้านครั้งและถึงอย่างนั้นก็ไม่น่าจะเป็นสาเหตุของปัญหาคอขวด


5

หมายเหตุเพิ่มเติมเนื่องจากคำตอบอื่น ๆ ตอบคำถามของคุณเป็นส่วนใหญ่อย่างถูกต้องคือถ้าชั้นเรียนมีการกำหนดเพียงอย่างเดียว__eq__()และไม่ใช่__ne__()คุณCOMPARE_OP (!=)จะต้องดำเนินการ__eq__()และปฏิเสธมัน ในเวลานั้นตัวเลือกที่สามของคุณน่าจะมีประสิทธิภาพมากกว่านิดหน่อย แต่ควรได้รับการพิจารณาก็ต่อเมื่อคุณต้องการความเร็วเนื่องจากมันยากที่จะเข้าใจอย่างรวดเร็ว


3

มันเกี่ยวกับวิธีการอ่านของคุณ notตัวดำเนินการเป็นแบบไดนามิกนั่นคือสาเหตุที่คุณสามารถนำไปใช้งานได้

if not x == 'val':

แต่!=สามารถอ่านในบริบทที่ดีกว่าที่เป็นผู้ประกอบการซึ่งจะตรงกันข้ามกับสิ่งที่==ทำ


3
คุณหมายถึงอะไร" notผู้ดำเนินการเป็นแบบไดนามิก" ?
jonrsharpe

1
@ jonrsharpe ฉันคิดว่าเขาหมายความว่า "ไม่ใช่ x" จะเรียก x .__ bool __ () [python 3 - python 2 ใช้nonzero ] และกลับผลลัพธ์ (ดูdocs.python.org/3/reference/datamodel.html#object __bool__ )
jdferreira

1

ฉันต้องการขยายความคิดเห็นที่สามารถอ่านได้ด้านบน

อีกครั้งฉันเห็นด้วยอย่างสมบูรณ์กับความสามารถในการอ่านแทนที่ข้อกังวลอื่น ๆ (ไม่สำคัญต่อประสิทธิภาพ)

สิ่งที่ฉันต้องการชี้ให้เห็นคือสมองตีความ "บวก" ได้เร็วกว่า "ลบ" เช่น "หยุด" และ "ไม่ไป" (เป็นตัวอย่างที่ค่อนข้างมีหมัดเนื่องจากความแตกต่างของจำนวนคำ)

ได้รับทางเลือก:

if a == b
    (do this)
else
    (do that)

จะดีกว่าที่เทียบเท่ากับหน้าที่:

if a != b
    (do that)
else
    (do this)

ความสามารถในการอ่าน / ทำความเข้าใจน้อยนำไปสู่ข้อบกพร่องมากขึ้น อาจไม่ได้อยู่ในการเข้ารหัสเริ่มต้น แต่การบำรุงรักษา (ไม่ฉลาดเท่าคุณ!) เปลี่ยนแปลง ...


1
สมองแปลความ "เป็นบวก" ได้เร็วกว่า "ลบ" เป็นสิ่งนี้มาจากประสบการณ์หรือคุณอ่านการศึกษาเกี่ยวกับเรื่องนี้หรือไม่? ฉันแค่ถามเพราะขึ้นอยู่กับรหัสใน (ทำสิ่งนี้) หรือ (ทำอย่างนั้น) ฉันพบว่า! = b ง่ายต่อการเข้าใจ
lafferc
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.