Asosiatif dari "in" dengan Python?

107

Saya membuat parser Python, dan ini benar - benar membingungkan saya:

>>>  1 in  []  in 'a'
False

>>> (1 in  []) in 'a'
TypeError: 'in <string>' requires string as left operand, not bool

>>>  1 in ([] in 'a')
TypeError: 'in <string>' requires string as left operand, not list

Bagaimana tepatnya "dalam" bekerja dengan Python, terkait dengan asosiatif, dll.?

Mengapa tidak ada dua dari ekspresi ini yang berperilaku sama?

pengguna541686
sumber
6
Anda mungkin mencapai perilaku yang dijelaskan di sini: docs.python.org/reference/expressions.html#not-in , perilaku yang memungkinkan Anda menulis if a < b < c:dan membuatnya bekerja secara intuitif
millimoose
3
@millimoose: Ya, saya tidak pernah berpikir insebagai operator "pembanding". : \
user541686

Jawaban:

123

1 in [] in 'a'dievaluasi sebagai (1 in []) and ([] in 'a').

Karena kondisi pertama ( 1 in []) adalah False, seluruh ketentuan dievaluasi sebagai False; ([] in 'a')tidak pernah benar-benar dievaluasi, jadi tidak ada kesalahan yang muncul.

Berikut adalah definisi pernyataannya:

In [121]: def func():
   .....:     return 1 in [] in 'a'
   .....: 

In [122]: dis.dis(func)
  2           0 LOAD_CONST               1 (1)
              3 BUILD_LIST               0
              6 DUP_TOP             
              7 ROT_THREE           
              8 COMPARE_OP               6 (in)
             11 JUMP_IF_FALSE            8 (to 22)  #if first comparison is wrong 
                                                    #then jump to 22, 
             14 POP_TOP             
             15 LOAD_CONST               2 ('a')
             18 COMPARE_OP               6 (in)     #this is never executed, so no Error
             21 RETURN_VALUE         
        >>   22 ROT_TWO             
             23 POP_TOP             
             24 RETURN_VALUE        

In [150]: def func1():
   .....:     return (1 in  []) in 'a'
   .....: 

In [151]: dis.dis(func1)
  2           0 LOAD_CONST               1 (1)
              3 LOAD_CONST               3 (())
              6 COMPARE_OP               6 (in)   # perform 1 in []
              9 LOAD_CONST               2 ('a')  # now load 'a'
             12 COMPARE_OP               6 (in)   # compare result of (1 in []) with 'a'
                                                  # throws Error coz (False in 'a') is
                                                  # TypeError
             15 RETURN_VALUE   



In [153]: def func2():
   .....:     return 1 in ([] in 'a')
   .....: 

In [154]: dis.dis(func2)
  2           0 LOAD_CONST               1 (1)
              3 BUILD_LIST               0
              6 LOAD_CONST               2 ('a') 
              9 COMPARE_OP               6 (in)  # perform ([] in 'a'), which is 
                                                 # Incorrect, so it throws TypeError
             12 COMPARE_OP               6 (in)  # if no Error then 
                                                 # compare 1 with the result of ([] in 'a')
             15 RETURN_VALUE        
Ashwini Chaudhary
sumber
Wah !! +1 Luar biasa, terima kasih banyak! Ini terlihat sangat berguna, jika saja saya tahu tentang itu! Apakah Anda kebetulan tahu di mana ini ada di dokumentasi? Saya melihat tetapi tidak dapat menemukan apa pun yang menyarankan ini!
pengguna541686
1
catatan: []salah, tetapi []bukan Falsemisalnya, [] and anythingkembali [](tidak False).
jfs
6
@Mehrdad Lihat Python disassembler yang digunakan dengan iPython untuk menghasilkan keluaran ini.
Jeff Ferland
Entah versi apa dari Python yang menghasilkan ini, tetapi Python 3.2 tampaknya memiliki bytecode baru: JUMP_IF_FALSE_OR_POP, yang memperpendek urutannya dengan satu instruksi dari 13 menjadi 12. Jawaban keren - terima kasih !!
Dave
@Dave Ini python 2.6.6 (iPython)
Ashwini Chaudhary
22

Python melakukan hal-hal khusus dengan perbandingan berantai.

Berikut ini dievaluasi secara berbeda:

x > y > z   # in this case, if x > y evaluates to true, then
            # the value of y is being used to compare, again,
            # to z

(x > y) > z # the parenth form, on the other hand, will first
            # evaluate x > y. And, compare the evaluated result
            # with z, which can be "True > z" or "False > z"

Namun dalam kedua kasus tersebut, jika perbandingan pertama adalah False, pernyataan lainnya tidak akan dilihat.

Untuk kasus khusus Anda,

1 in [] in 'a'   # this is false because 1 is not in []

(1 in []) in a   # this gives an error because we are
                 # essentially doing this: False in 'a'

1 in ([] in 'a') # this fails because you cannot do
                 # [] in 'a'

Juga untuk mendemonstrasikan aturan pertama di atas, ini adalah pernyataan yang dievaluasi ke True.

1 in [1,2] in [4,[1,2]] # But "1 in [4,[1,2]]" is False

2 < 4 > 1               # and note "2 < 1" is also not true

Urutan operator python: http://docs.python.org/reference/expressions.html#summary

Alexander Chen
sumber
11

Dari dokumentasi:

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

Artinya, tidak ada asosiasi di dalam x in y in z!

Berikut ini adalah padanannya:

1 in  []  in 'a'
# <=>
middle = []
#            False          not evaluated
result = (1 in middle) and (middle in 'a')


(1 in  []) in 'a'
# <=>
lhs = (1 in []) # False
result = lhs in 'a' # False in 'a' - TypeError


1 in  ([] in 'a')
# <=>
rhs = ([] in 'a') # TypeError
result = 1 in rhs
phant0m
sumber
3

Jawaban singkatnya, karena yang lama sudah diberikan beberapa kali di sini dan dengan cara yang sangat baik, adalah ekspresi boolean dihubung pendek , ini telah menghentikan evaluasi ketika perubahan benar menjadi salah atau sebaliknya tidak dapat terjadi dengan evaluasi lebih lanjut.

(lihat http://en.wikipedia.org/wiki/Short-circuit_evaluation )

Ini mungkin sedikit pendek (tidak ada permainan kata-kata) sebagai jawaban, tetapi seperti yang disebutkan, semua penjelasan lainnya sudah dilakukan dengan cukup baik di sini, tetapi saya pikir istilah itu pantas untuk disebutkan.

Peter
sumber