ใน python 3.4 ฉันกำลังพิมพ์
[] = ""
และใช้งานได้ดีไม่มีการเพิ่มข้อยกเว้น แม้ว่า[]
จะไม่เท่ากับ""
หลังจากนั้นก็ตาม
[] = ()
ยังใช้งานได้ดี
"" = []
ทำให้เกิดข้อยกเว้นตามที่คาดไว้
() = ""
ทำให้เกิดข้อยกเว้นตามที่คาดไว้ เกิดอะไรขึ้น?
ใน python 3.4 ฉันกำลังพิมพ์
[] = ""
และใช้งานได้ดีไม่มีการเพิ่มข้อยกเว้น แม้ว่า[]
จะไม่เท่ากับ""
หลังจากนั้นก็ตาม
[] = ()
ยังใช้งานได้ดี
"" = []
ทำให้เกิดข้อยกเว้นตามที่คาดไว้
() = ""
ทำให้เกิดข้อยกเว้นตามที่คาดไว้ เกิดอะไรขึ้น?
คำตอบ:
คุณไม่ได้เปรียบเทียบเพื่อความเท่าเทียมกัน คุณกำลังกำหนด
Python ช่วยให้คุณกำหนดให้กับหลายเป้าหมาย:
foo, bar = 1, 2
กำหนดค่าสองค่าให้foo
และbar
ตามลำดับ สิ่งที่คุณต้องมีคือลำดับหรือทำซ้ำได้ทางด้านขวามือและรายชื่อหรือกลุ่มชื่อทางด้านซ้าย
เมื่อคุณทำ:
[] = ""
คุณกำหนดลำดับว่าง (สตริงว่างคือลำดับที่ยังคงอยู่) ให้กับรายการชื่อว่าง
โดยพื้นฐานแล้วมันก็เหมือนกับการทำ:
[foo, bar, baz] = "abc"
ที่คุณจะจบลงด้วยfoo = "a"
, bar = "b"
และbaz = "c"
แต่มีตัวละครน้อยลง
อย่างไรก็ตามคุณไม่สามารถกำหนดให้กับสตริงได้ดังนั้น""
ทางด้านซ้ายมือของงานจะไม่ทำงานและเป็นข้อผิดพลาดทางไวยากรณ์เสมอ
คำสั่งมอบหมายจะประเมินรายการนิพจน์ (โปรดจำไว้ว่านี่อาจเป็นนิพจน์เดียวหรือรายการที่คั่นด้วยเครื่องหมายจุลภาคส่วนหลังให้ค่าทูเปิล) และกำหนดอ็อบเจ็กต์ผลลัพธ์เดียวให้กับแต่ละรายการเป้าหมายจากซ้ายไปขวา
และ
การกำหนดอ็อบเจ็กต์ไปยังรายการเป้าหมายซึ่งเป็นทางเลือกที่อยู่ในวงเล็บหรือวงเล็บเหลี่ยมถูกกำหนดแบบวนซ้ำดังนี้
ผมขอย้ำ
Python ไม่ได้โยนข้อผิดพลาดทางไวยากรณ์สำหรับรายการว่างนั้นเป็นข้อผิดพลาดเล็กน้อย! ไวยากรณ์ที่จัดทำเป็นเอกสารอย่างเป็นทางการไม่อนุญาตให้มีรายการเป้าหมายว่างเปล่าและ()
คุณจะได้รับข้อผิดพลาด ดูข้อบกพร่อง 23275 ; ถือว่าเป็นข้อผิดพลาดที่ไม่เป็นอันตราย:
จุดเริ่มต้นคือการตระหนักว่าสิ่งนี้มีมานานมากและไม่เป็นอันตราย
ดูเพิ่มเติมว่าเหตุใดจึงถูกต้องที่จะกำหนดให้กับรายการว่าง แต่ไม่สามารถกำหนดให้กับทูเพิลว่างได้
เป็นไปตามกฎส่วนงบการมอบหมายจากเอกสารประกอบ
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()