Mengapa ekspresi 0 <0 == 0 mengembalikan False dengan Python?

138

Melihat ke Queue.py dengan Python 2.6, saya menemukan konstruksi yang menurut saya agak aneh:

def full(self):
    """Return True if the queue is full, False otherwise
    (not reliable!)."""
    self.mutex.acquire()
    n = 0 < self.maxsize == self._qsize()
    self.mutex.release()
    return n

Jika maxsize0 antrian tidak pernah penuh.

Pertanyaan saya adalah bagaimana cara kerjanya untuk kasus ini? Bagaimana 0 < 0 == 0dianggap Salah?

>>> 0 < 0 == 0
False
>>> (0) < (0 == 0)
True
>>> (0 < 0) == 0
True
>>> 0 < (0 == 0)
True
Marcelo Santos
sumber
Apakah 0 <True sama dengan False di python?
Marino Šimić
3
@Marino Šimić: Dari contoh kedua yang ditunjukkan dalam pertanyaan OP >>> (0) < (0 == 0), jelas tidak.
martineau
3
Salah satu alasan Anda tidak boleh menulis kode seperti n = 0 < self.maxsize == self._qsize()di tempat pertama, dalam bahasa apa pun. Jika mata Anda harus melesat bolak-balik melintasi garis beberapa kali untuk mencari tahu apa yang terjadi, itu bukan baris yang ditulis dengan baik. Pisahkan saja menjadi beberapa baris.
BlueRaja - Danny Pflughoeft
2
@Blue: Saya setuju dengan tidak menulis perbandingan seperti itu tetapi membaginya menjadi baris terpisah akan sedikit berlebihan untuk dua perbandingan. Saya harap maksud Anda, bagi menjadi perbandingan terpisah. ;)
Jeff Mercado
2
@Blue: Saya tidak menulisnya, ini menggunakan Python 2.6. Saya hanya mencoba untuk memahami apa yang sedang terjadi.
Marcelo Santos

Jawaban:

114

Saya percaya Python memiliki penanganan kasus khusus untuk urutan operator relasional untuk membuat perbandingan jangkauan mudah diungkapkan. Jauh lebih baik untuk mengatakan 0 < x <= 5daripada mengatakan (0 < x) and (x <= 5).

Ini disebut perbandingan berantai . Dan itu adalah tautan ke dokumentasi untuk mereka.

Dengan kasus lain yang Anda bicarakan, tanda kurung memaksa satu operator relasional diterapkan sebelum yang lain, sehingga mereka tidak lagi menjadi perbandingan berantai. Dan karena Truedan Falsememiliki nilai sebagai bilangan bulat Anda mendapatkan jawaban yang Anda lakukan dari versi tanda kurung.

Beraneka ragam
sumber
menarik untuk mencoba beberapa perbandingan ini dan menentukan int () dan bool (). Saya menyadari bahwa bool () dari setiap bukan nol adalah 1. Sepertinya saya tidak akan pernah mencoba secara langsung menentukan apa pun selain bool (0) atau bool (1) sebelum eksperimen pemikiran ini
j_syk
Apakah Anda bermaksud untuk menautkan ke bagian ini? docs.python.org/2/reference/expressions.html#comparisons
tavnab
@tavnab - Ya. Saya akan mencoba mengingat untuk memperbaikinya. Saya juga akan memeriksa riwayat edit. Sepertinya itu bukan kesalahan yang akan saya buat. 😕
Omnifarious
42

Karena

(0 < 0) and (0 == 0)

adalah False. Anda dapat menyatukan operator perbandingan dan mereka secara otomatis diperluas menjadi perbandingan berpasangan.


EDIT - klarifikasi tentang Benar dan Salah dengan Python

Dengan Python Truedan Falsehanya contoh bool, yang merupakan subclass dari int. Dengan kata lain, Truesebenarnya hanya 1.

Intinya adalah Anda dapat menggunakan hasil perbandingan boolean persis seperti bilangan bulat. Ini mengarah pada hal-hal yang membingungkan seperti

>>> (1==1)+(1==1)
2
>>> (2<1)<1
True

Tapi ini hanya akan terjadi jika Anda mengurung perbandingan sehingga dievaluasi terlebih dahulu. Jika tidak, Python akan memperluas operator perbandingan.

Katriel
sumber
2
Saya melihat penggunaan yang menarik untuk nilai boolean yang digunakan sebagai bilangan bulat kemarin. Ungkapan tersebut 'success' if result_code == 0 else 'failure'dapat ditulis ulang karena ('error', 'success')[result_code == 0], sebelumnya saya belum pernah melihat boolean yang digunakan untuk memilih item dalam daftar / tupel.
Andrew Clark
'bool' ditambahkan sekitar Python 2.2.
MRAB
18

Perilaku aneh yang Anda alami berasal dari kemampuan ular piton hingga kondisi rantai. Karena menemukan 0 tidak kurang dari 0, ia memutuskan bahwa seluruh ekspresi bernilai false. Segera setelah Anda memecahnya menjadi kondisi terpisah, Anda mengubah fungsinya. Ini pada dasarnya adalah mengujinya a < b && b == cuntuk pernyataan asli Anda a < b == c.

Contoh lain:

>>> 1 < 5 < 3
False

>>> (1 < 5) < 3
True
Tyler
sumber
1
OMG, a < b && b == csama dengan a < b == cOO
Kiril Kirov
9
>>> 0 < 0 == 0
False

Ini adalah perbandingan yang dirantai. Ini mengembalikan nilai benar jika setiap perbandingan berpasangan pada gilirannya benar. Itu sama dengan(0 < 0) and (0 == 0)

>>> (0) < (0 == 0)
True

Ini sama dengan 0 < Trueyang mengevaluasi ke True.

>>> (0 < 0) == 0
True

Ini sama dengan False == 0yang mengevaluasi ke True.

>>> 0 < (0 == 0)
True

Setara dengan 0 < Trueyang, seperti di atas, mengevaluasi ke True.

David Heffernan
sumber
7

Melihat pembongkaran (byte kode) itu jelas mengapa 0 < 0 == 0adalah False.

Berikut adalah analisis dari ungkapan ini:

>>>import dis

>>>def f():
...    0 < 0 == 0

>>>dis.dis(f)
  2      0 LOAD_CONST               1 (0)
         3 LOAD_CONST               1 (0)
         6 DUP_TOP
         7 ROT_THREE
         8 COMPARE_OP               0 (<)
        11 JUMP_IF_FALSE_OR_POP    23
        14 LOAD_CONST               1 (0)
        17 COMPARE_OP               2 (==)
        20 JUMP_FORWARD             2 (to 25)
   >>   23 ROT_TWO
        24 POP_TOP
   >>   25 POP_TOP
        26 LOAD_CONST               0 (None)
        29 RETURN_VALUE

Perhatikan baris 0-8: Baris-baris ini memeriksa apakah 0 < 0yang jelas kembali Falseke tumpukan python.

Sekarang perhatikan baris 11: JUMP_IF_FALSE_OR_POP 23 Ini berarti bahwa jika 0 < 0kembali Falsemelakukan lompatan ke baris 23.

Sekarang, 0 < 0adalah False, jadi lompatan diambil, yang meninggalkan tumpukan dengan Falseyang merupakan nilai kembalian untuk seluruh ekspresi 0 < 0 == 0, meskipun == 0bagian tersebut bahkan tidak dicentang.

Jadi, untuk menyimpulkan, jawabannya seperti yang dikatakan dalam jawaban lain atas pertanyaan ini. 0 < 0 == 0memiliki arti khusus. Kompilator mengevaluasi ini menjadi dua istilah: 0 < 0dan 0 == 0. Seperti halnya ekspresi boolean kompleks di andantara mereka, jika yang pertama gagal maka yang kedua bahkan tidak dicentang.

Berharap ini mencerahkan segalanya, dan saya sangat berharap metode yang saya gunakan untuk menganalisis perilaku tak terduga ini akan mendorong orang lain untuk mencoba hal yang sama di masa depan.

SatA
sumber
Bukankah lebih mudah untuk mengerjakannya dari spesifikasi daripada merekayasa balik satu implementasi tertentu?
David Heffernan
Tidak. Itulah jawaban singkatnya. Saya percaya itu tergantung pada kepribadian Anda. Jika Anda berhubungan dengan tampilan "kotak hitam" dan lebih suka mendapatkan jawaban Anda dari spesifikasi dan dokumentasi, jawaban ini hanya akan membingungkan Anda. Jika Anda suka menggali dan mengungkapkan bagian dalam suatu barang, maka jawaban ini untuk Anda. Pernyataan Anda bahwa rekayasa balik ini hanya relevan untuk satu penerapan tertentu adalah benar dan harus ditunjukkan, tetapi bukan itu inti dari jawaban ini. Ini adalah demonstrasi tentang betapa mudahnya python untuk melihat "di bawah tenda" bagi mereka yang cukup penasaran.
SatA
1
Masalah dengan rekayasa balik adalah kurangnya daya prediksi. Ini bukan cara untuk mempelajari bahasa baru.
David Heffernan
Hal lain yang harus saya tambahkan adalah bahwa spesifikasi dan dokumentasi tidak selalu lengkap dan dalam banyak kasus tidak akan memberikan jawaban untuk kasus-kasus tertentu. Kemudian, saya yakin, seseorang tidak perlu takut untuk mengeksplorasi, menyelidiki, dan menggali lebih dalam sebanyak yang dibutuhkan untuk mendapatkan jawabannya.
SatA
2

Seperti yang disebutkan orang lain x comparison_operator y comparison_operator zadalah gula sintaksis (x comparison_operator y) and (y comparison_operator z)dengan bonus yang hanya dievaluasi sekali.

Jadi ekspresi Anda 0 < 0 == 0benar-benar (0 < 0) and (0 == 0)mengevaluasi False and Truemana yang adil False.

dr jimbob
sumber
2

mungkin kutipan dari dokumen ini dapat membantu:

Ini adalah apa yang disebut metode "perbandingan kaya", dan dipanggil untuk operator perbandingan dengan preferensi di __cmp__()bawah ini. Korespondensi antara simbol operator dan nama metode adalah sebagai berikut: x<ypanggilan x.__lt__(y), x<=ypanggilan x.__le__(y), x==ypanggilan x.__eq__(y), x!=ydan x<>y panggilan x.__ne__(y), x>ypanggilan x.__gt__(y), dan x>=ypanggilan x.__ge__(y).

Metode perbandingan yang kaya dapat mengembalikan singleton NotImplementedjika tidak mengimplementasikan operasi untuk pasangan argumen tertentu. Sesuai kesepakatan , Falsedan Truedikembalikan untuk perbandingan yang berhasil. Namun, metode ini dapat mengembalikan nilai apa pun, jadi jika operator perbandingan digunakan dalam konteks Boolean (misalnya, dalam kondisi pernyataan if), Python akan memanggil bool()nilai tersebut untuk menentukan apakah hasilnya benar atau salah.

Tidak ada hubungan tersirat di antara operator pembanding. Kebenaran x==ytidak berarti bahwa x!=y itu salah. Oleh karena itu, saat mendefinisikan __eq__(), seseorang juga harus mendefinisikan __ne__()sehingga operator akan berperilaku seperti yang diharapkan. Lihat paragraf __hash__()untuk beberapa catatan penting tentang membuat objek hashable yang mendukung operasi perbandingan kustom dan dapat digunakan sebagai kunci kamus.

Tidak ada versi argumen yang ditukar dari metode ini (untuk digunakan ketika argumen kiri tidak mendukung operasi tetapi argumen kanan mendukung); melainkan, __lt__()dan __gt__() merupakan cerminan satu sama lain, __le__() dan __ge__()merupakan cerminan satu sama lain, __eq__()dan __ne__() merupakan cerminan mereka sendiri.

Argumen metode perbandingan kaya tidak pernah dipaksakan.

Ini adalah perbandingan, tetapi karena Anda merangkai perbandingan, Anda harus tahu bahwa:

Perbandingan dapat dirangkai secara sewenang-wenang, misalnya, x < y <= zekuivalen dengan x < y and y <= z, kecuali bahwa y hanya dievaluasi sekali (tetapi dalam kedua kasus z tidak dievaluasi sama sekali ketika x <y ditemukan salah).

Secara formal, jika a, b, c, ..., y, z adalah ekspresi dan op1, op2, ..., opN adalah operator pembanding, maka op1 b op2 c ... y opN z setara dengan op1 b dan b op2 c dan ... y opN z, kecuali bahwa setiap ekspresi dievaluasi paling banyak sekali.

Marino Šimić
sumber
1

Ini dia, dengan segala kemuliaannya.

>>> class showme(object):
...   def __init__(self, name, value):
...     self.name, self.value = name, value
...   def __repr__(self):
...     return "<showme %s:%s>" % (self.name, self.value)
...   def __cmp__(self, other):
...     print "cmp(%r, %r)" % (self, other)
...     if type(other) == showme:
...       return cmp(self.value, other.value)
...     else:
...       return cmp(self.value, other)
... 
>>> showme(1,0) < showme(2,0) == showme(3,0)
cmp(<showme 1:0>, <showme 2:0>)
False
>>> (showme(1,0) < showme(2,0)) == showme(3,0)
cmp(<showme 1:0>, <showme 2:0>)
cmp(<showme 3:0>, False)
True
>>> showme(1,0) < (showme(2,0) == showme(3,0))
cmp(<showme 2:0>, <showme 3:0>)
cmp(<showme 1:0>, True)
True
>>> 
SingleNegationElimination
sumber
0

Saya pikir Python melakukan itu aneh antara sihir. Sama seperti 1 < 2 < 3mean 2 adalah antara 1 dan 3.

Dalam kasus ini, menurut saya [tengah 0] lebih besar dari [kiri 0] dan sama dengan [kanan 0]. Tengah 0 tidak lebih besar dari kiri 0, sehingga bernilai false.

mpen
sumber