Python nếu không == vs if! =


183

Sự khác biệt giữa hai dòng mã này là gì:

if not x == 'val':

if x != 'val':

Là một trong những hiệu quả hơn so với khác?

Nó sẽ tốt hơn để sử dụng

if x == 'val':
    pass
else:

101
Cái tốt hơn là cái bạn có thể đọc, tôi nghi ngờ nút cổ chai của chương trình của bạn sẽ ở đây
Thomas Ayoub

1
Câu hỏi này khiến tôi quan tâm đến trường hợp "x không có trong danh sách" và "không x trong danh sách"
SomethingS Something

5
@ Một cái gì đó họ được giải thích giống hệt nhau.
jonrsharpe

4
@S SomethingS Something tham khảo cho nhận xét trên của tôi: stackoverflow.com/q/8738388/3001761
jonrsharpe

1
@S SomethingS Something cũng giống như vậy đối với những người đó; đó là cách cú pháp được diễn giải, không quan trọng hai toán hạng là gì.
jonrsharpe

Câu trả lời:


229

Sử dụng disđể xem mã byte được tạo cho hai phiên bản:

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   

Cái sau có ít hoạt động hơn, và do đó có khả năng sẽ hiệu quả hơn một chút.


Điều này đã được chỉ ra trong các cam kết (cảm ơn, @Quincunx ) rằng nơi bạn có if foo != barso với if not foo == barsố lượng hoạt động là hoàn toàn giống nhau, đó chỉ là những COMPARE_OPthay đổi và POP_JUMP_IF_TRUEchuyển sang 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

Trong trường hợp này, trừ khi có sự khác biệt về số lượng công việc cần thiết cho mỗi so sánh, không chắc bạn sẽ thấy bất kỳ sự khác biệt nào về hiệu suất.


Tuy nhiên, lưu ý rằng hai phiên bản sẽ không luôn giống nhau về mặt logic , vì nó sẽ phụ thuộc vào việc triển khai __eq____ne__cho các đối tượng được đề cập. Theo tài liệu mô hình dữ liệu :

Không có mối quan hệ ngụ ý giữa các nhà khai thác so sánh. Sự thật x==ykhông ngụ ý đó x!=ylà sai.

Ví dụ:

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


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

Cuối cùng, và có lẽ quan trọng nhất là: nói chung, nơi hai logic giống nhau, x != ylà nhiều hơn nữa có thể đọc hơnnot x == y .


29
Trong thực tế, bất kỳ lớp học nào __eq__không phù hợp __ne__đều bị phá vỡ.
Kevin

8
Xin lưu ý rằng không phải lúc nào cũng đúng mà not x == ycó thêm một hướng dẫn. Khi tôi đặt mã vào một if, hóa ra cả hai đều có cùng một số hướng dẫn, chỉ một cái có POP_JUMP_IF_TRUEvà cái kia POP_JUMP_IF_FALSE(đó là sự khác biệt duy nhất giữa chúng, ngoài việc sử dụng khác nhau COMPARE_OP). Khi tôi biên dịch mã mà không có ifs, tôi đã nhận được những gì bạn có.
Justin

1
Một ví dụ khác trong đó ==!=không loại trừ lẫn nhau là một triển khai giống như SQL liên quan đến nullcác giá trị. Trong SQL nullkhông trả lại trueđể !=so sánh với bất kỳ giá trị khác, vì vậy việc triển khai python giao diện SQL cũng có thể có cùng một vấn đề.
Joe

Tôi bắt đầu ước mình đã không đề cập đến sự khác biệt có thể có giữa not ==!=, nó dường như là phần thú vị nhất trong câu trả lời của tôi! Tôi không nghĩ rằng đây là nơi để sống nếu, tại sao và khi điều đó có ý nghĩa - xem ví dụ: Tại sao Python có một __ne__phương thức toán tử thay vì chỉ __eq__?
jonrsharpe

29

@jonrsharpe có một lời giải thích tuyệt vời về những gì đang xảy ra. Tôi nghĩ rằng tôi chỉ thể hiện sự khác biệt về thời gian khi chạy mỗi trong số 3 tùy chọn 10.000.000 lần (đủ để hiển thị một chút khác biệt).

Mã đã được sử dụng:

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)

Và kết quả hồ sơ cProfile:

nhập mô tả hình ảnh ở đây

Vì vậy, chúng ta có thể thấy rằng có một sự khác biệt rất nhỏ trong khoảng ~ 0,7% giữa if not x == 'val':if x != 'val':. Trong số này, if x != 'val':là nhanh nhất.

Tuy nhiên, đáng ngạc nhiên nhất, chúng ta có thể thấy rằng

if x == 'val':
        pass
    else:

trên thực tế là nhanh nhất và nhịp đập if x != 'val':~ 0,3%. Điều này không dễ đọc lắm, nhưng tôi đoán nếu bạn muốn cải thiện hiệu suất không đáng kể, người ta có thể đi theo con đường này.


31
Tôi hy vọng mọi người biết không hành động về thông tin này! Thực hiện các thay đổi không thể đọc được để cải thiện 0,3% - hoặc thậm chí là cải thiện 10% - hiếm khi là một ý tưởng hay và loại cải tiến này rất có thể sẽ biến mất (và không phải là một cách tốt : những thay đổi rất nhỏ trong thời gian chạy Python có thể loại bỏ hoặc thậm chí đảo ngược bất kỳ lợi ích nào.
Malvolio

1
@Malvolio Ngoài ra, có nhiều cách triển khai Python khác nhau .
Cees Timmerman

6

Trong lần đầu tiên, Python phải thực thi thêm một thao tác hơn mức cần thiết (thay vì chỉ kiểm tra không bằng nó phải kiểm tra xem có đúng không nếu nó bằng nhau, do đó thêm một thao tác nữa). Không thể nói sự khác biệt từ một lần thực hiện, nhưng nếu chạy nhiều lần, lần thứ hai sẽ hiệu quả hơn. Nhìn chung, tôi sẽ sử dụng cái thứ hai, nhưng về mặt toán học thì chúng giống nhau


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

Ở đây bạn có thể thấy rằng not x == ycó một hướng dẫn nhiều hơn x != y. Vì vậy, sự khác biệt hiệu suất sẽ rất nhỏ trong hầu hết các trường hợp trừ khi bạn đang thực hiện hàng triệu so sánh và thậm chí sau đó điều này có thể sẽ không phải là nguyên nhân của nút cổ chai.


5

Một lưu ý bổ sung, vì các câu trả lời khác trả lời chính xác câu hỏi của bạn, là nếu một lớp chỉ xác định __eq__()và không __ne__(), thì bạn COMPARE_OP (!=)sẽ chạy __eq__()và phủ nhận nó. Vào thời điểm đó, tùy chọn thứ ba của bạn có thể sẽ hiệu quả hơn một chút, nhưng chỉ nên được xem xét nếu bạn CẦN tốc độ, vì nó khó hiểu một cách nhanh chóng.


3

Đó là về cách đọc của bạn. notToán tử là động, đó là lý do tại sao bạn có thể áp dụng nó trong

if not x == 'val':

Nhưng !=có thể được đọc trong một bối cảnh tốt hơn như là một toán tử mà ngược lại với những gì ==làm.


3
Bạn có nghĩa là " nottoán tử là năng động" ?
jonrsharpe

1
@jonrsharpe Tôi nghĩ rằng anh ta có nghĩa là "không phải x" sẽ gọi x .__ bool __ () [python 3 - python 2 sử dụng nonzero ] và hoàn nguyên kết quả (xem docs.python.org/3/reference/datamodel.html#object. __bool__ )
jdferreira

1

Tôi muốn mở rộng về nhận xét dễ đọc của tôi ở trên.

Một lần nữa, tôi hoàn toàn đồng ý với khả năng đọc ghi đè các mối quan tâm khác (không đáng kể về hiệu suất).

Điều tôi muốn chỉ ra là bộ não diễn giải "tích cực" nhanh hơn "tiêu cực". Ví dụ: "dừng" so với "không đi" (một ví dụ khá tệ hại do sự khác biệt về số lượng từ).

Vì vậy, đưa ra một sự lựa chọn:

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

thích hợp hơn cho chức năng tương đương:

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

Ít khả năng đọc / dễ hiểu dẫn đến nhiều lỗi hơn. Có lẽ không phải trong mã hóa ban đầu, nhưng việc bảo trì (không thông minh như bạn!) Thay đổi ...


1
bộ não diễn giải "tích cực" nhanh hơn "tiêu cực" là từ kinh nghiệm này hay bạn đã đọc các nghiên cứu về điều này? Tôi chỉ hỏi vì tùy thuộc vào mã trong (làm điều này) hoặc (làm điều đó), tôi thấy a! = B dễ hiểu hơn.
lafferc
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.