ทำไมใน Python จึง“ 0, 0 == (0, 0)” เท่ากับ“ (0, False)”


118

ใน Python (ฉันตรวจสอบด้วย Python 3.6 เท่านั้น แต่ฉันเชื่อว่ามันควรจะเก็บไว้สำหรับเวอร์ชันก่อนหน้านี้ด้วย):

(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

แต่:

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

เหตุใดผลลัพธ์จึงแตกต่างกันระหว่างสองแนวทาง? ตัวดำเนินการความเท่าเทียมกันจัดการกับสิ่งที่ต่างกันหรือไม่?

คำตอบ:


156

สองนิพจน์แรกทั้งสองแยกวิเคราะห์เป็นทูเปิล:

  1. (0, 0) == 0(ซึ่งก็คือFalse) ตามด้วย0
  2. 0ตามด้วย0 == (0, 0)(ซึ่งยังคงเป็นเช่นFalseนั้น)

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

แต่ในงบชุดที่สองของa = 0, 0 คุณไม่สามารถเป็นทูเพิลได้ ทูเปิลคือชุดของค่าและไม่เหมือนกับการทดสอบความเท่าเทียมกันการกำหนดไม่มีค่าใน Python การมอบหมายงานไม่ใช่นิพจน์ แต่เป็นคำสั่ง ไม่มีค่าที่สามารถรวมไว้ในทูเพิลหรือนิพจน์อื่น ๆ โดยรอบ หากคุณลองทำบางอย่างเช่น(a = 0), 0เพื่อบังคับให้ตีความเป็นทูเพิลคุณจะได้รับข้อผิดพลาดทางไวยากรณ์ ที่ใบโอน tuple ตัวแปรหนึ่ง - ซึ่งอาจจะทำอย่างชัดเจนมากขึ้นโดยการเขียนมันa = (0, 0)- a = 0, 0เป็นความหมายที่ถูกต้องเพียงอย่างเดียว

ดังนั้นแม้ว่าจะไม่มีวงเล็บในการกำหนดให้aทั้งมันและbได้รับการกำหนดค่า(0,0)ดังนั้นจึงa == bเป็นเช่นTrueนั้น


17
ฉันจะบอกว่าตัวดำเนินการลูกน้ำมีความสำคัญต่ำกว่าความเท่าเทียมกันเนื่องจากการประเมินความเท่าเทียมกันนำหน้าตัวดำเนินการลูกน้ำ: ความเท่าเทียมกันมีความสำคัญสูงกว่าตัวดำเนินการลูกน้ำ แต่นี่เป็นที่มาของความสับสนเสมอ เพียงแค่ต้องการชี้ให้เห็นว่าแหล่งข้อมูลอื่นอาจพลิกสิ่งต่างๆ
tomsmeding

2
คุณสามารถหลีกเลี่ยงที่ต่ำกว่า / ความสับสนการใช้คำฟุ่มเฟือยที่สูงขึ้นโดยแทนที่จะบอกว่าผูกแน่นน้อยกว่า, ==
amalloy

4
ลูกน้ำไม่ใช่ตัวดำเนินการdocs.python.org/3.4/faq/…
Chris_Rands

48
เอกสารสามารถอ้างว่าทั้งหมดที่พวกเขาต้องการ แต่มันไม่สำคัญ คุณสามารถเขียนตัวแยกวิเคราะห์เพื่อให้โอเปอเรเตอร์ทุกตัวได้รับการผลิตของตัวเองและไม่มี "ลำดับความสำคัญ" ที่ชัดเจนใด ๆ ในการใช้งาน แต่นั่นไม่ได้ป้องกันไม่ให้หน่วยไวยากรณ์เหล่านั้นเป็นตัวดำเนินการคุณสามารถกำหนด "ตัวดำเนินการ" ใหม่ได้ในวิธีการใช้งานเฉพาะบางวิธี ซึ่งเห็นได้ชัดว่าพวกเขาทำอะไรใน Python แต่นั่นไม่ได้เปลี่ยนความหมายของคำศัพท์ ลูกน้ำเป็นตัวดำเนินการที่สร้างสิ่งมีชีวิตได้อย่างมีประสิทธิภาพ ตัวอย่างเช่นการทำงานของมันแสดงให้เห็นว่าลำดับความสำคัญสัมพัทธ์ได้รับผลกระทบจากวงเล็บอย่างไร
Mark Reed

68

สิ่งที่คุณเห็นในทั้ง 3 อินสแตนซ์เป็นผลมาจากข้อกำหนดทางไวยากรณ์ของภาษาและวิธีการแยกวิเคราะห์โทเค็นที่พบในซอร์สโค้ดเพื่อสร้างแผนผังการแยกวิเคราะห์

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

กรณีที่ 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)เป็นครั้งแรกที่เมื่อเทียบกับครั้งแรกและการประเมินเพื่อ0 Falseสิ่งอันดับถูกสร้างแล้วกับผลนี้และที่ผ่านมาเพื่อให้คุณได้รับ0(False, 0)

กรณีที่ 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

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

กรณีที่ 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

ที่นี่เป็นที่คุณเห็นคุณเพียงแค่เปรียบเทียบทั้งสอง(0, 0)tuples Trueและกลับมา


20

อีกวิธีหนึ่งในการอธิบายปัญหา: คุณอาจคุ้นเคยกับพจนานุกรม

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

และอาร์เรย์ตัวอักษร

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

และตัวอักษรทูเพิล

( 1, 2, 3 )

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

1, 2, 3

("exprlist" ในภาษาของไวยากรณ์ที่เป็นทางการสำหรับ Python )

ตอนนี้คุณคาดหวังอะไรกับตัวอักษรอาร์เรย์

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

เพื่อประเมิน? นั่นอาจจะดูเหมือนว่ามันควรจะเหมือนกับ

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

[0, False]ซึ่งประเมินหลักสูตรเพื่อ ในทำนองเดียวกันมีตัวอักษรทูเพิลในวงเล็บอย่างชัดเจน

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

(0, False)มันไม่น่าแปลกใจที่จะได้รับ แต่วงเล็บเป็นทางเลือก

0, 0 == (0, 0)

เป็นสิ่งเดียวกัน (0, False)และนั่นเป็นเหตุผลที่คุณจะได้รับ


หากคุณสงสัยว่าเหตุใดวงเล็บรอบตัวอักษรทูเพิลจึงเป็นทางเลือกส่วนใหญ่เป็นเพราะมันน่ารำคาญที่ต้องเขียนการมอบหมายการทำลายด้วยวิธีนี้:

(a, b) = (c, d) # meh
a, b = c, d     # better

17

การเพิ่มวงเล็บรอบลำดับการดำเนินการอาจช่วยให้คุณเข้าใจผลลัพธ์ได้ดีขึ้น:

# 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

เครื่องหมายจุลภาคใช้เพื่อแยกนิพจน์ (แน่นอนว่าการใช้วงเล็บเราสามารถบังคับให้เกิดพฤติกรรมที่แตกต่างกันได้) เมื่อดูตัวอย่างข้อมูลที่คุณแสดงไว้เครื่องหมายจุลภาค,จะแยกและกำหนดนิพจน์ที่จะได้รับการประเมิน:

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

ทูเปิล(0, 0)ยังสามารถแบ่งย่อยในลักษณะเดียวกัน 0เครื่องหมายจุลภาคแยกสองสำนวนที่ประกอบด้วยตัวอักษร


6

ใน Python ตัวแรกกำลังสร้างทูเพิลสองสิ่ง:

  1. นิพจน์(0, 0) == 0ซึ่งประเมินเป็นFalse
  2. ค่าคงที่ 0

อันที่สองมันเป็นอีกทางหนึ่ง


0

ดูตัวอย่างนี้:

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)

แล้วผลลัพธ์:

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

จากนั้นเปรียบเทียบกับตัวเลขแรก (0 และ r) ในตัวอย่าง

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