ความเชื่อมโยงของ“ in” ใน Python?


107

ฉันกำลังสร้างตัวแยกวิเคราะห์ Python และสิ่งนี้ทำให้ฉันสับสนจริงๆ :

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

"ใน" ทำงานอย่างไรใน Python เกี่ยวกับการเชื่อมโยง ฯลฯ

เหตุใดจึงไม่มีการแสดงออกทั้งสองแบบนี้ในลักษณะเดียวกัน


6
คุณอาจกดปุ่มพฤติกรรมที่อธิบายไว้ที่นี่: docs.python.org/reference/expressions.html#not-inซึ่งช่วยให้คุณเขียนif a < b < c:และทำให้มันทำงานได้โดยสัญชาตญาณ
มิลลิโมส

3
@millimoose: ใช่ฉันไม่เคยคิดว่าจะinเป็นตัวดำเนินการ "เปรียบเทียบ" ฉันเดา : \
user541686

คำตอบ:


123

1 in [] in 'a'(1 in []) and ([] in 'a')มีการประเมินว่า

เนื่องจากเงื่อนไขแรก ( 1 in []) คือFalseเงื่อนไขทั้งหมดจึงถูกประเมินเป็นFalse; ([] in 'a')ไม่เคยได้รับการประเมินจริงดังนั้นจึงไม่มีข้อผิดพลาดเกิดขึ้น

นี่คือคำจำกัดความของคำสั่ง:

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        

โว้ว!! +1 สุดยอดมากขอบคุณมาก! มันดูมีประโยชน์จริงๆถ้าฉันรู้เรื่องนี้! คุณบังเอิญรู้หรือไม่ว่าสิ่งนี้อยู่ที่ไหนในเอกสารนี้? ฉันมองหาแต่ไม่พบสิ่งที่แนะนำสิ่งนี้!
user541686

1
หมายเหตุ: []เป็นเท็จ แต่[]ไม่ใช่Falseเช่น[] and anythingส่งกลับ[](ไม่False)
jfs

6
@Mehrdad ตรวจสอบPython disassemblerที่ใช้กับiPythonเพื่อสร้างเอาต์พุตนี้
Jeff Ferland

Dunno เวอร์ชันใดของ Python ที่สร้างสิ่งนี้ แต่ Python 3.2 เห็นได้ชัดว่ามี bytecode ใหม่: JUMP_IF_FALSE_OR_POP ซึ่งจะย่อลำดับโดยหนึ่งคำสั่งจาก 13 เป็น 12 คำตอบที่ยอดเยี่ยม - ขอบคุณ !!
Dave

@Dave มันหลาม 2.6.6 (iPython)
Ashwini Chaudhary

22

Python ทำสิ่งพิเศษด้วยการเปรียบเทียบแบบล่ามโซ่

สิ่งต่อไปนี้ได้รับการประเมินแตกต่างกัน:

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"

ในทั้งสองกรณีหากเป็นการเปรียบเทียบครั้งแรกFalseส่วนที่เหลือของข้อความจะไม่ถูกพิจารณา

สำหรับกรณีเฉพาะของคุณ

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'

นอกจากนี้เพื่อแสดงให้เห็นกฎข้อแรกข้างต้นนี่คือข้อความที่ประเมินเป็น 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

ลำดับความสำคัญของตัวดำเนินการ python: http://docs.python.org/reference/expressions.html#summary


11

จากเอกสารประกอบ:

การเปรียบเทียบสามารถผูกมัดได้ตามอำเภอใจเช่น x <y <= z เทียบเท่ากับ x <y และ y <= z ยกเว้นว่า y จะถูกประเมินเพียงครั้งเดียว (แต่ในทั้งสองกรณี z จะไม่ได้รับการประเมินเลยเมื่อพบ x <y เป็นเท็จ)

สิ่งนี้หมายความว่าไม่มีการเชื่อมโยงx in y in z!

สิ่งต่อไปนี้เทียบเท่า:

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

3

คำตอบสั้น ๆ เนื่องจากคำตอบแบบยาวได้รับมาแล้วหลายครั้งที่นี่และในรูปแบบที่ยอดเยี่ยมคือนิพจน์บูลีนนั้น ลัดวงจรสิ่งนี้จึงหยุดการประเมินเมื่อการเปลี่ยนแปลงของจริงเป็นเท็จหรือในทางกลับกันไม่สามารถเกิดขึ้นได้โดยการประเมินเพิ่มเติม

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

คำตอบอาจสั้นไปหน่อย (ไม่ได้ตั้งใจเล่นสำนวน) แต่ตามที่กล่าวไว้คำอธิบายอื่น ๆ ทั้งหมดทำได้ค่อนข้างดีที่นี่ แต่ฉันคิดว่าคำนี้สมควรได้รับการกล่าวถึง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.