Mengapa di Python "0, 0 == (0, 0)" sama dengan "(0, False)"?

118

Dengan Python (saya hanya memeriksa dengan Python 3.6 tetapi saya yakin itu harus berlaku untuk banyak versi sebelumnya juga):

(0, 0) == 0, 0   # results in a two element tuple: (False, 0)
0, 0 == (0, 0)   # results in a two element tuple: (0, False)
(0, 0) == (0, 0) # results in a boolean True

Tapi:

a = 0, 0
b = (0, 0)
a == b # results in a boolean True

Mengapa hasilnya berbeda antara kedua pendekatan tersebut? Apakah operator persamaan menangani tupel secara berbeda?

Piotr Zakrzewski
sumber

Jawaban:

156

Dua ekspresi pertama sama-sama mengurai sebagai tupel:

  1. (0, 0) == 0(yaitu False), diikuti oleh0
  2. 0, diikuti oleh 0 == (0, 0)(yang masih Falseseperti itu).

Ekspresi dibagi seperti itu karena prioritas relatif pemisah koma dibandingkan dengan operator kesetaraan: Python melihat tupel yang berisi dua ekspresi, salah satunya kebetulan merupakan uji kesetaraan, bukan uji kesetaraan antara dua tupel.

Tetapi dalam set pernyataan kedua Anda, a = 0, 0 tidak bisa menjadi tupel. Tuple adalah sekumpulan nilai, dan tidak seperti uji kesetaraan, tugas tidak memiliki nilai dalam Python. Penugasan bukanlah ekspresi, tapi pernyataan; itu tidak memiliki nilai yang dapat dimasukkan ke dalam tupel atau ekspresi sekitarnya lainnya. Jika Anda mencoba sesuatu seperti (a = 0), 0untuk memaksa interpretasi sebagai tupel, Anda akan mendapatkan kesalahan sintaks. Itu membuat penugasan tupel ke variabel - yang bisa dibuat lebih eksplisit dengan menulisnya a = (0, 0)- sebagai satu-satunya interpretasi yang valid a = 0, 0.

Jadi, bahkan tanpa tanda kurung pada tugas ke a, baik itu dan bmendapatkan nilai yang ditetapkan (0,0), begitu a == bjuga True.

Mark Reed
sumber
17
Saya akan mengatakan bahwa operator koma memiliki prioritas yang lebih rendah daripada persamaan, karena evaluasi persamaan mendahului operator koma: persamaan memiliki prioritas yang lebih tinggi daripada operator koma. Tapi ini selalu menjadi sumber kebingungan; hanya ingin menunjukkan bahwa sumber lain mungkin membalikkan keadaan.
tomsmeding
2
Anda dapat menghindari kebingungan bertele-tele yang lebih rendah / lebih tinggi dengan mengatakan bahwa ,mengikat kurang dari ==.
amalloy
4
Koma bukan merupakan operator docs.python.org/3.4/faq/…
Chris_Rands
48
Dokumen dapat mengklaim semua yang mereka inginkan, tetapi itu tidak masalah. Anda dapat menulis parser sehingga setiap operator mendapatkan produksinya sendiri dan tidak ada "prioritas" yang eksplisit di mana pun dalam implementasi, tetapi itu tidak mencegah unit sintaksis tersebut menjadi operator. Anda dapat mendefinisikan ulang "operator" dalam beberapa cara khusus implementasi , yang tampaknya mereka lakukan dengan Python, tetapi itu tidak mengubah implikasi istilah tersebut. Koma secara efektif merupakan operator yang menghasilkan tupel. Operasinya terlihat dalam, misalnya, cara prioritas relatifnya dipengaruhi oleh tanda kurung.
Mark Reed
68

Apa yang Anda lihat di semua 3 contoh adalah konsekuensi dari spesifikasi tata bahasa, dan bagaimana token yang ditemukan dalam kode sumber diurai untuk menghasilkan pohon parse.

Melihat kode tingkat rendah ini akan membantu Anda memahami apa yang terjadi di balik terpal. Kita dapat mengambil pernyataan python ini, mengubahnya menjadi kode byte dan kemudian mendekompilasinya menggunakan dismodul:

Kasus 1: (0, 0) == 0, 0

>>> dis.dis(compile("(0, 0) == 0, 0", '', 'exec'))
  1           0 LOAD_CONST               2 ((0, 0))
              3 LOAD_CONST               0 (0)
              6 COMPARE_OP               2 (==)
              9 LOAD_CONST               0 (0)
             12 BUILD_TUPLE              2
             15 POP_TOP
             16 LOAD_CONST               1 (None)
             19 RETURN_VALUE

(0, 0)pertama kali dibandingkan dengan yang 0pertama dan dievaluasi False. Sebuah tupel kemudian dibangun dengan hasil ini dan yang terakhir 0, jadi Anda dapatkan (False, 0).

Kasus 2: 0, 0 == (0, 0)

>>> dis.dis(compile("0, 0 == (0, 0)", '', 'exec'))
  1           0 LOAD_CONST               0 (0)
              3 LOAD_CONST               0 (0)
              6 LOAD_CONST               2 ((0, 0))
              9 COMPARE_OP               2 (==)
             12 BUILD_TUPLE              2
             15 POP_TOP
             16 LOAD_CONST               1 (None)
             19 RETURN_VALUE

Sebuah tupel dibangun dengan 0sebagai elemen pertama. Untuk elemen kedua, pemeriksaan yang sama dilakukan seperti pada kasus pertama dan dievaluasi False, sehingga Anda mendapatkan (0, False).

Kasus 3: (0, 0) == (0, 0)

>>> dis.dis(compile("(0, 0) == (0, 0)", '', 'exec'))
  1           0 LOAD_CONST               2 ((0, 0))
              3 LOAD_CONST               3 ((0, 0))
              6 COMPARE_OP               2 (==)
              9 POP_TOP
             10 LOAD_CONST               1 (None)
             13 RETURN_VALUE

Di sini, seperti yang Anda lihat, Anda hanya membandingkan kedua (0, 0)tupel itu dan kembali True.

cs95
sumber
20

Cara lain untuk menjelaskan masalah: Anda mungkin sudah familiar dengan kamus literal

{ "a": 1, "b": 2, "c": 3 }

dan literal array

[ "a", "b", "c" ]

dan literal tupel

( 1, 2, 3 )

tapi yang tidak Anda sadari adalah, tidak seperti kamus dan literal array, tanda kurung yang biasanya Anda lihat di sekitar literal tupel bukanlah bagian dari sintaks literal . Sintaks literal untuk tupel hanyalah urutan ekspresi yang dipisahkan oleh koma:

1, 2, 3

(sebuah "exprlist" dalam bahasa tata bahasa formal untuk Python ).

Sekarang, apa yang Anda harapkan dari array literal

[ 0, 0 == (0, 0) ]

untuk mengevaluasi? Itu mungkin terlihat lebih seperti seharusnya sama

[ 0, (0 == (0, 0)) ]

yang tentu saja bernilai [0, False]. Demikian pula, dengan literal tupel yang diberi tanda kurung secara eksplisit

( 0, 0 == (0, 0) )

tidak mengherankan untuk mendapatkannya (0, False). Tetapi tanda kurung bersifat opsional;

0, 0 == (0, 0)

adalah hal yang sama. Dan itulah mengapa Anda mendapatkannya (0, False).


Jika Anda bertanya-tanya mengapa tanda kurung di sekitar literal tupel bersifat opsional, itu sebagian besar karena akan menjengkelkan jika harus menulis tugas yang merusak seperti itu:

(a, b) = (c, d) # meh
a, b = c, d     # better
zwol
sumber
17

Menambahkan beberapa tanda kurung di sekitar urutan tindakan dilakukan dapat membantu Anda memahami hasil dengan lebih baik:

# Build two element tuple comprising of 
# (0, 0) == 0 result and 0
>>> ((0, 0) == 0), 0
(False, 0)

# Build two element tuple comprising of
# 0 and result of (0, 0) == 0 
>>> 0, (0 == (0, 0))
(0, False)

# Create two tuples with elements (0, 0) 
# and compare them
>>> (0, 0) == (0, 0) 
True

Koma digunakan untuk memisahkan ekspresi (menggunakan tanda kurung kita dapat memaksa perilaku yang berbeda, tentunya). Saat melihat cuplikan yang Anda daftarkan, koma ,akan memisahkannya dan menentukan ekspresi apa yang akan dievaluasi:

(0, 0) == 0 ,   0
#-----------|------
  expr 1      expr2

Tupel (0, 0)juga dapat dipecah dengan cara yang sama. Koma memisahkan dua ekspresi yang terdiri dari literal 0.

Dimitris Fasarakis Hilliard
sumber
6

Yang pertama Python membuat tupel dari dua hal:

  1. Ekspresi (0, 0) == 0, yang mengevaluasi keFalse
  2. Konstan 0

Yang kedua adalah sebaliknya.

kindall
sumber
0

lihat contoh ini:

r = [1,0,1,0,1,1,0,0,0,1]
print(r==0,0,r,1,0)
print(r==r,0,1,0,1,0)

kemudian hasilnya:

False 0 [1, 0, 1, 0, 1, 1, 0, 0, 0, 1] 1 0
True 0 1 0 1 0

kemudian perbandingan hanya dilakukan pada bilangan pertama (0 dan r) pada contoh.

Emad Saeidi
sumber