อะไรคือความแตกต่างระหว่างโค้ดสองบรรทัดนี้:
if not x == 'val':
และ
if x != 'val':
หนึ่งมีประสิทธิภาพมากกว่าอื่น ๆ
มันจะดีกว่าที่จะใช้
if x == 'val':
pass
else:
อะไรคือความแตกต่างระหว่างโค้ดสองบรรทัดนี้:
if not x == 'val':
และ
if x != 'val':
หนึ่งมีประสิทธิภาพมากกว่าอื่น ๆ
มันจะดีกว่าที่จะใช้
if x == 'val':
pass
else:
คำตอบ:
การใช้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 != y
not x == y
__eq__
ไม่สอดคล้องกับ__ne__
นั้นจะถูกแบน
not x == y
มีคำแนะนำอีกหนึ่ง เมื่อฉันใส่รหัสลงในif
มันกลับกลายเป็นว่าพวกเขาทั้งสองมีจำนวนคำสั่งเท่ากันมีเพียงคำสั่งเดียวPOP_JUMP_IF_TRUE
และอีกอันหนึ่งPOP_JUMP_IF_FALSE
(นั่นเป็นข้อแตกต่างระหว่างพวกเขาCOMPARE_OP
เท่านั้น เมื่อฉันรวบรวมรหัสโดยไม่ต้องif
s ฉันได้สิ่งที่คุณได้รับ
==
และ!=
ไม่ได้เกิดร่วมกันเป็นพิเศษคือการใช้งานแบบ SQL ที่เกี่ยวข้องกับnull
ค่า ใน SQL null
จะไม่กลับtrue
ไป!=
เปรียบเทียบกับค่าอื่น ๆ ดังนั้นการใช้ python ของอินเตอร์เฟส SQL อาจมีปัญหาเดียวกัน
not ==
และ!=
ดูเหมือนว่าจะเป็นส่วนที่น่าสนใจที่สุดของคำตอบของฉัน! ฉันไม่คิดว่านี่เป็นสถานที่ที่จะอยู่ทำไมและเมื่อใดที่เหมาะสม - ดูเช่นทำไม Python จึงมี__ne__
วิธีการใช้งานแทนเพียงแค่__eq__
?
@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% นี่ไม่สามารถอ่านได้มาก แต่ฉันเดาว่าถ้าคุณต้องการการปรับปรุงประสิทธิภาพที่ไม่สำคัญใคร ๆ ก็สามารถลงเส้นทางนี้ได้
ใน Python แรกจะต้องดำเนินการมากกว่าหนึ่งอย่างที่จำเป็น (แทนที่จะเพียงแค่ตรวจสอบไม่เท่ากับมันต้องตรวจสอบว่ามันไม่เป็นความจริงว่ามันเท่ากันดังนั้นจึงเป็นการดำเนินการอีกหนึ่งครั้ง) มันจะเป็นไปไม่ได้ที่จะบอกความแตกต่างจากการประหารชีวิตครั้งเดียว แต่ถ้าวิ่งไปหลายครั้งสิ่งที่สองจะมีประสิทธิภาพมากกว่า โดยรวมแล้วฉันจะใช้อันที่สอง แต่ในทางคณิตศาสตร์พวกมันเหมือนกัน
>>> 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
หนึ่ง ดังนั้นความแตกต่างด้านประสิทธิภาพจะเล็กมากในกรณีส่วนใหญ่เว้นแต่คุณจะทำการเปรียบเทียบหลายล้านครั้งและถึงอย่างนั้นก็ไม่น่าจะเป็นสาเหตุของปัญหาคอขวด
หมายเหตุเพิ่มเติมเนื่องจากคำตอบอื่น ๆ ตอบคำถามของคุณเป็นส่วนใหญ่อย่างถูกต้องคือถ้าชั้นเรียนมีการกำหนดเพียงอย่างเดียว__eq__()
และไม่ใช่__ne__()
คุณCOMPARE_OP (!=)
จะต้องดำเนินการ__eq__()
และปฏิเสธมัน ในเวลานั้นตัวเลือกที่สามของคุณน่าจะมีประสิทธิภาพมากกว่านิดหน่อย แต่ควรได้รับการพิจารณาก็ต่อเมื่อคุณต้องการความเร็วเนื่องจากมันยากที่จะเข้าใจอย่างรวดเร็ว
มันเกี่ยวกับวิธีการอ่านของคุณ not
ตัวดำเนินการเป็นแบบไดนามิกนั่นคือสาเหตุที่คุณสามารถนำไปใช้งานได้
if not x == 'val':
แต่!=
สามารถอ่านในบริบทที่ดีกว่าที่เป็นผู้ประกอบการซึ่งจะตรงกันข้ามกับสิ่งที่==
ทำ
not
ผู้ดำเนินการเป็นแบบไดนามิก" ?
ฉันต้องการขยายความคิดเห็นที่สามารถอ่านได้ด้านบน
อีกครั้งฉันเห็นด้วยอย่างสมบูรณ์กับความสามารถในการอ่านแทนที่ข้อกังวลอื่น ๆ (ไม่สำคัญต่อประสิทธิภาพ)
สิ่งที่ฉันต้องการชี้ให้เห็นคือสมองตีความ "บวก" ได้เร็วกว่า "ลบ" เช่น "หยุด" และ "ไม่ไป" (เป็นตัวอย่างที่ค่อนข้างมีหมัดเนื่องจากความแตกต่างของจำนวนคำ)
ได้รับทางเลือก:
if a == b
(do this)
else
(do that)
จะดีกว่าที่เทียบเท่ากับหน้าที่:
if a != b
(do that)
else
(do this)
ความสามารถในการอ่าน / ทำความเข้าใจน้อยนำไปสู่ข้อบกพร่องมากขึ้น อาจไม่ได้อยู่ในการเข้ารหัสเริ่มต้น แต่การบำรุงรักษา (ไม่ฉลาดเท่าคุณ!) เปลี่ยนแปลง ...