เหตุใดจึงไม่กำหนดให้รายการว่าง (เช่น [] =“”) เกิดข้อผิดพลาด


110

ใน python 3.4 ฉันกำลังพิมพ์

[] = "" 

และใช้งานได้ดีไม่มีการเพิ่มข้อยกเว้น แม้ว่า[]จะไม่เท่ากับ""หลังจากนั้นก็ตาม

[] = ()

ยังใช้งานได้ดี

"" = []

ทำให้เกิดข้อยกเว้นตามที่คาดไว้

() = ""

ทำให้เกิดข้อยกเว้นตามที่คาดไว้ เกิดอะไรขึ้น?

คำตอบ:


132

คุณไม่ได้เปรียบเทียบเพื่อความเท่าเทียมกัน คุณกำลังกำหนด

Python ช่วยให้คุณกำหนดให้กับหลายเป้าหมาย:

foo, bar = 1, 2

กำหนดค่าสองค่าให้fooและbarตามลำดับ สิ่งที่คุณต้องมีคือลำดับหรือทำซ้ำได้ทางด้านขวามือและรายชื่อหรือกลุ่มชื่อทางด้านซ้าย

เมื่อคุณทำ:

[] = ""

คุณกำหนดลำดับว่าง (สตริงว่างคือลำดับที่ยังคงอยู่) ให้กับรายการชื่อว่าง

โดยพื้นฐานแล้วมันก็เหมือนกับการทำ:

[foo, bar, baz] = "abc"

ที่คุณจะจบลงด้วยfoo = "a", bar = "b"และbaz = "c"แต่มีตัวละครน้อยลง

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

ดูเอกสารคำสั่ง Assignment :

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

และ

การกำหนดอ็อบเจ็กต์ไปยังรายการเป้าหมายซึ่งเป็นทางเลือกที่อยู่ในวงเล็บหรือวงเล็บเหลี่ยมถูกกำหนดแบบวนซ้ำดังนี้

ผมขอย้ำ

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

จุดเริ่มต้นคือการตระหนักว่าสิ่งนี้มีมานานมากและไม่เป็นอันตราย

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


36

เป็นไปตามกฎส่วนงบการมอบหมายจากเอกสารประกอบ

assignment_stmt ::=  (target_list "=")+ (expression_list | yield_expression)

หากtarget listเป็นรายการเป้าหมายที่คั่นด้วยเครื่องหมายจุลภาค: วัตถุจะต้องทำซ้ำได้โดยมีจำนวนรายการเดียวกันกับที่มีเป้าหมายในรายการเป้าหมายและมีการกำหนดรายการจากซ้ายไปขวาไปยังเป้าหมายที่เกี่ยวข้อง

ออบเจ็กต์ต้องเป็นลำดับที่มีจำนวนรายการเท่ากันเนื่องจากมีเป้าหมายในรายการเป้าหมายและมีการกำหนดรายการจากซ้ายไปขวาไปยังเป้าหมายที่เกี่ยวข้อง

ดังนั้นเมื่อคุณพูด

[] = ""

"" สามารถทำซ้ำได้ (สตริง python ที่ถูกต้องสามารถทำซ้ำได้) และจะถูกคลายออกจากองค์ประกอบของรายการ

ตัวอย่างเช่น,

>>> [a, b, c] = "123"
>>> a, b, c
('1', '2', '3')

เนื่องจากคุณมีสตริงว่างและรายการว่างจึงไม่มีอะไรต้องแกะ ดังนั้นไม่มีข้อผิดพลาด

แต่ลองดูสิ

>>> [] = "1"
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: too many values to unpack (expected 0)
>>> [a] = ""
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: need more than 0 values to unpack

ใน[] = "1"กรณีนี้คุณกำลังพยายามคลายสตริง"1"บนรายการตัวแปรที่ว่างเปล่า ดังนั้นจึงบ่นว่า "มีค่ามากเกินไปที่จะแกะ (คาดว่าจะเป็น 0)"

ใน[a] = ""กรณีเดียวกันคุณมีสตริงว่างจึงไม่มีอะไรต้องแกะจริงๆ แต่คุณกำลังแกะออกจากตัวแปรหนึ่งตัวซึ่งเป็นไปไม่ได้อีกแล้ว นั่นคือเหตุผลที่มันบ่นว่า "ต้องการค่ามากกว่า 0 เพื่อแกะกล่อง"

นอกเหนือจากนั้นอย่างที่คุณสังเกตเห็น

>>> [] = ()

ยังไม่มีข้อผิดพลาดเนื่องจาก()เป็นทูเปิลที่ว่างเปล่า

>>> ()
()
>>> type(())
<class 'tuple'>

และเมื่อมันถูกคลายออกจากรายการที่ว่างเปล่าจะไม่มีอะไรต้องแกะ จึงไม่มีข้อผิดพลาด


แต่เมื่อคุณทำ

>>> "" = []
  File "<input>", line 1
SyntaxError: can't assign to literal
>>> "" = ()
  File "<input>", line 1
SyntaxError: can't assign to literal

ตามข้อความแสดงข้อผิดพลาดคุณกำลังพยายามกำหนดให้กับสตริงลิเทอรัล ซึ่งเป็นไปไม่ได้. นั่นคือเหตุผลที่คุณได้รับข้อผิดพลาด เป็นอย่างที่บอก

>>> 1 = "one"
  File "<input>", line 1
SyntaxError: can't assign to literal

ภายใน

ภายในการดำเนินการมอบหมายนี้จะถูกแปลเป็นUNPACK_SEQUENCEรหัส op

>>> dis(compile('[] = ""', "string", "exec"))
  1           0 LOAD_CONST               0 ('')
              3 UNPACK_SEQUENCE          0
              6 LOAD_CONST               1 (None)

ที่นี่เนื่องจากสตริงว่างเปล่าจึงUNPACK_SEQUENCEคลายแพ็ก0ครั้ง แต่เมื่อคุณมีสิ่งนี้

>>> dis(compile('[a, b, c] = "123"', "string", "exec"))
  1           0 LOAD_CONST               0 ('123')
              3 UNPACK_SEQUENCE          3
              6 STORE_NAME               0 (a)
              9 STORE_NAME               1 (b)
             12 STORE_NAME               2 (c)
             15 LOAD_CONST               1 (None)
             18 RETURN_VALUE

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


BTW ใน Python นี่คือวิธีที่คุณสามารถทำงานหลาย ๆ งานในนิพจน์เดียวกันได้ ตัวอย่างเช่น,

a, b, c, d, e, f = u, v, w, x, y, z

ใช้งานได้เนื่องจากค่าทางขวามือถูกใช้เพื่อสร้างทูเปิลจากนั้นจะถูกคลายออกจากค่าทางด้านซ้ายมือ

>>> dis(compile('a, b, c, d, e, f = u, v, w, x, y, z', "string", "exec"))
  1           0 LOAD_NAME                0 (u)
              3 LOAD_NAME                1 (v)
              6 LOAD_NAME                2 (w)
              9 LOAD_NAME                3 (x)
             12 LOAD_NAME                4 (y)
             15 LOAD_NAME                5 (z)
             18 BUILD_TUPLE              6
             21 UNPACK_SEQUENCE          6
             24 STORE_NAME               6 (a)
             27 STORE_NAME               7 (b)
             30 STORE_NAME               8 (c)
             33 STORE_NAME               9 (d)
             36 STORE_NAME              10 (e)
             39 STORE_NAME              11 (f)
             42 LOAD_CONST               0 (None)
             45 RETURN_VALUE

แต่เทคนิคการแลกเปลี่ยนแบบคลาสสิกa, b = b, aใช้การหมุนองค์ประกอบที่ด้านบนของสแต็ก หากคุณมีเพียงสองหรือสามองค์ประกอบพวกเขาจะได้รับการปฏิบัติเป็นพิเศษROT_TWOและROT_THREEคำแนะนำแทนที่จะสร้างทูเพิลและแกะกล่อง

>>> dis(compile('a, b = b, a', "string", "exec"))
  1           0 LOAD_NAME                0 (b)
              3 LOAD_NAME                1 (a)
              6 ROT_TWO
              7 STORE_NAME               1 (a)
             10 STORE_NAME               0 (b)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE

นอกจากนี้คุณยังสามารถใช้โดยไม่ต้องโทรdis('[] = ""') compile()
Andrea Corbellini

คุณอธิบายได้ไหมว่าจะเกิดอะไรขึ้นหากคุณสลับตัวแปร / องค์ประกอบมากกว่าสามตัวโดยใช้วิธีการในตัวอย่างสุดท้ายของคุณ
nanofarad

@hexafraction มันจะสร้างทูเพิลใหม่โดยมีองค์ประกอบทั้งหมดทางด้านขวามือจากนั้นมันจะแกะมันออกมาทับตัวแปรทางด้านซ้ายมือ
thefourtheye

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