Python if not == vs if! =

183

Apa perbedaan antara dua baris kode ini:

if not x == 'val':

dan

if x != 'val':

Apakah yang satu lebih efisien daripada yang lain?

Apakah lebih baik digunakan

if x == 'val':
    pass
else:
lafferc
sumber
101
Semakin baik yang bisa Anda baca, saya ragu hambatan program Anda ada di sini
Thomas Ayoub
1
Pertanyaan ini menarik minat saya dalam kasus "x tidak ada dalam daftar" dan "tidak dalam daftar"
SomethingSomething
5
@ Sesuatu. Sesuatu, mereka ditafsirkan secara identik.
jonrsharpe
4
@SomethingSomething referensi untuk komentar saya di atas: stackoverflow.com/q/8738388/3001761
jonrsharpe
1
@ Sesuatu Sesuatu juga sama bagi mereka; itu adalah bagaimana sintaks diinterpretasikan, tidak peduli apa kedua operan itu.
jonrsharpe

Jawaban:

229

Menggunakan disuntuk melihat bytecode yang dihasilkan untuk dua versi:

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   

Yang terakhir memiliki operasi lebih sedikit, dan karena itu cenderung sedikit lebih efisien


Itu ditunjukkan dalam komitmen (terima kasih, @ Quincunx ) bahwa di mana Anda memiliki if foo != barvs if not foo == barjumlah operasi persis sama, hanya saja COMPARE_OPperubahan dan POP_JUMP_IF_TRUEberalih ke 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

Dalam hal ini, kecuali ada perbedaan dalam jumlah pekerjaan yang diperlukan untuk setiap perbandingan, kecil kemungkinan Anda akan melihat perbedaan kinerja sama sekali.


Namun, perhatikan bahwa kedua versi tidak akan selalu identik secara logis , karena akan tergantung pada implementasi dari __eq__dan __ne__untuk objek yang dimaksud. Per dokumentasi model data :

Tidak ada hubungan yang tersirat di antara operator pembanding. Kebenaran x==ytidak menyiratkan bahwa x!=yitu salah.

Sebagai contoh:

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


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

Akhirnya, dan mungkin yang paling penting: secara umum, di mana dua yang logis identik, x != yjauh lebih mudah dibaca daripadanot x == y .

Jonrsharpe
sumber
29
Dalam praktiknya, setiap kelas yang __eq__tidak konsisten dengan __ne__itu benar-benar rusak.
Kevin
8
Harap dicatat bahwa tidak selalu benar bahwa not x == ymemiliki satu instruksi lagi. Ketika saya memasukkan kode ke dalam if, ternyata mereka berdua memiliki jumlah instruksi yang sama, hanya satu yang memiliki POP_JUMP_IF_TRUEdan yang lainnya POP_JUMP_IF_FALSE(itu adalah satu-satunya perbedaan di antara mereka, selain menggunakan yang berbeda COMPARE_OP). Ketika saya mengkompilasi kode tanpa ifs, saya mendapatkan apa yang Anda dapatkan.
Justin
1
Contoh lain di mana ==dan !=tidak saling eksklusif adalah implementasi SQL-seperti yang melibatkan nullnilai - nilai. Dalam SQL nulltidak kembali trueke !=dibandingkan dengan nilai lain, sehingga implementasi python interface SQL mungkin juga memiliki masalah yang sama.
Joe
Aku mulai berharap aku tidak menyebut kemungkinan perbedaan di antara not ==dan !=, sepertinya itu menjadi bagian paling menarik dari jawabanku! Saya tidak berpikir ini adalah tempat untuk memikirkan jika, mengapa dan kapan itu masuk akal - lihat misalnya Mengapa Python memiliki __ne__metode operator bukan hanya __eq__?
jonrsharpe
29

@jonrsharpe memiliki penjelasan yang bagus tentang apa yang terjadi. Saya pikir saya hanya akan menunjukkan perbedaan waktu ketika menjalankan masing-masing dari 3 opsi 10.000.000 kali (cukup untuk menunjukkan sedikit perbedaan).

Kode yang digunakan:

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)

Dan profil profil cProfile:

masukkan deskripsi gambar di sini

Jadi kita dapat melihat bahwa ada perbedaan sangat kecil ~ 0,7% antara if not x == 'val':dan if x != 'val':. Dari jumlah tersebut, if x != 'val':adalah yang tercepat.

Namun, yang paling mengejutkan, kita bisa melihatnya

if x == 'val':
        pass
    else:

sebenarnya yang tercepat, dan berdetak if x != 'val':~ 0,3%. Ini tidak terlalu mudah dibaca, tetapi saya kira jika Anda menginginkan peningkatan kinerja yang dapat diabaikan, orang dapat menempuh rute ini.

Pergeseran Merah
sumber
31
Saya harap semua orang tahu untuk tidak bertindak berdasarkan informasi ini! Membuat perubahan yang tidak dapat dibaca untuk peningkatan 0,3% - atau bahkan peningkatan 10% - jarang merupakan ide yang baik, dan perbaikan semacam ini sangat mungkin cepat berlalu drastis (dan tidak dengan cara yang baik : perubahan yang sangat kecil dalam runtime Python bisa menghilangkan atau bahkan membalikkan perolehan apa pun
Malvolio
1
@Malvolio Selain itu, ada implementasi berbeda dari Python.
Cees Timmerman
6

Dalam yang pertama Python harus menjalankan satu operasi lebih dari yang diperlukan (bukan hanya memeriksa tidak sama dengan itu harus memeriksa apakah itu tidak benar bahwa itu sama, dengan demikian satu operasi lagi). Tidak mungkin mengatakan perbedaan dari satu eksekusi, tetapi jika dijalankan berkali-kali, yang kedua akan lebih efisien. Secara keseluruhan saya akan menggunakan yang kedua, tetapi secara matematis keduanya sama

JediPythonClone
sumber
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

Di sini Anda dapat melihat bahwa not x == ymemiliki satu instruksi lebih dari x != y. Jadi perbedaan kinerja akan sangat kecil dalam banyak kasus kecuali jika Anda melakukan jutaan perbandingan dan bahkan kemudian ini kemungkinan tidak akan menjadi penyebab kemacetan.

kylie.a
sumber
5

Catatan tambahan, karena jawaban lain menjawab sebagian besar pertanyaan Anda dengan benar, adalah bahwa jika sebuah kelas hanya mendefinisikan __eq__()dan tidak __ne__(), maka Anda COMPARE_OP (!=)akan menjalankan __eq__()dan meniadakannya. Pada saat itu, opsi ketiga Anda cenderung menjadi sedikit lebih efisien, tetapi seharusnya hanya dipertimbangkan jika Anda MEMBUTUHKAN kecepatan, karena sulit untuk dipahami dengan cepat.

Jacob Zimmerman
sumber
3

Ini tentang cara Anda membacanya. notoperator itu dinamis, itu sebabnya Anda bisa menerapkannya

if not x == 'val':

Tetapi !=dapat dibaca dalam konteks yang lebih baik sebagai operator yang melakukan kebalikan dari apa yang ==dilakukan.

Himanshu Mishra
sumber
3
Apa maksudmu " notoperator itu dinamis" ?
jonrsharpe
1
@jonrsharpe Saya pikir maksudnya "bukan x" akan memanggil x .__ bool __ () [python 3 - python 2 menggunakan bukan nol ] dan mengembalikan hasilnya (lihat docs.python.org/3/reference/datamodel.html#object. __bool__ )
jdferreira
1

Saya ingin memperluas komentar keterbacaan saya di atas.

Sekali lagi, saya sepenuhnya setuju dengan keterbacaan yang mengesampingkan masalah lain (kinerja-tidak signifikan).

Yang ingin saya tunjukkan adalah otak mengartikan "positif" lebih cepat daripada "negatif". Misalnya, "berhenti" vs. "jangan pergi" (contoh yang agak buruk karena perbedaan jumlah kata).

Jadi diberi pilihan:

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

lebih disukai daripada yang secara fungsional setara:

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

Kurang mudah dibaca / dimengerti menyebabkan lebih banyak bug. Mungkin tidak dalam pengkodean awal, tetapi pemeliharaan (tidak sepintar Anda!) Berubah ...

Alan Jay Weiner
sumber
1
otak mengartikan "positif" lebih cepat daripada "negatif" apakah ini dari pengalaman atau pernahkah Anda membaca studi tentang ini? Saya hanya bertanya karena tergantung pada kode di (lakukan ini) atau (lakukan itu), saya menemukan! = B lebih mudah dimengerti.
lafferc