การเปิดออกการขยายการเปิดออกและการขยายแบบซ้อนกัน


105

พิจารณานิพจน์ต่อไปนี้ โปรดทราบว่าบางนิพจน์ซ้ำเพื่อนำเสนอ "บริบท"

(นี่คือรายการยาว)

a, b = 1, 2                          # simple sequence assignment
a, b = ['green', 'blue']             # list asqignment
a, b = 'XY'                          # string assignment
a, b = range(1,5,2)                  # any iterable will do


                                     # nested sequence assignment

(a,b), c = "XY", "Z"                 # a = 'X', b = 'Y', c = 'Z' 

(a,b), c = "XYZ"                     # ERROR -- too many values to unpack
(a,b), c = "XY"                      # ERROR -- need more than 1 value to unpack

(a,b), c, = [1,2],'this'             # a = '1', b = '2', c = 'this'
(a,b), (c,) = [1,2],'this'           # ERROR -- too many values to unpack


                                     # extended sequence unpacking

a, *b = 1,2,3,4,5                    # a = 1, b = [2,3,4,5]
*a, b = 1,2,3,4,5                    # a = [1,2,3,4], b = 5
a, *b, c = 1,2,3,4,5                 # a = 1, b = [2,3,4], c = 5

a, *b = 'X'                          # a = 'X', b = []
*a, b = 'X'                          # a = [], b = 'X'
a, *b, c = "XY"                      # a = 'X', b = [], c = 'Y'
a, *b, c = "X...Y"                   # a = 'X', b = ['.','.','.'], c = 'Y'

a, b, *c = 1,2,3                     # a = 1, b = 2, c = [3]
a, b, c, *d = 1,2,3                  # a = 1, b = 2, c = 3, d = []

a, *b, c, *d = 1,2,3,4,5             # ERROR -- two starred expressions in assignment

(a,b), c = [1,2],'this'              # a = '1', b = '2', c = 'this'
(a,b), *c = [1,2],'this'             # a = '1', b = '2', c = ['this']

(a,b), c, *d = [1,2],'this'          # a = '1', b = '2', c = 'this', d = []
(a,b), *c, d = [1,2],'this'          # a = '1', b = '2', c = [], d = 'this'

(a,b), (c, *d) = [1,2],'this'        # a = '1', b = '2', c = 't', d = ['h', 'i', 's']

*a = 1                               # ERROR -- target must be in a list or tuple
*a = (1,2)                           # ERROR -- target must be in a list or tuple
*a, = (1,2)                          # a = [1,2]
*a, = 1                              # ERROR -- 'int' object is not iterable
*a, = [1]                            # a = [1]
*a = [1]                             # ERROR -- target must be in a list or tuple
*a, = (1,)                           # a = [1]
*a, = (1)                            # ERROR -- 'int' object is not iterable

*a, b = [1]                          # a = [], b = 1
*a, b = (1,)                         # a = [], b = 1

(a,b),c = 1,2,3                      # ERROR -- too many values to unpack
(a,b), *c = 1,2,3                    # ERROR - 'int' object is not iterable
(a,b), *c = 'XY', 2, 3               # a = 'X', b = 'Y', c = [2,3]


                                     # extended sequence unpacking -- NESTED

(a,b),c = 1,2,3                      # ERROR -- too many values to unpack
*(a,b), c = 1,2,3                    # a = 1, b = 2, c = 3

*(a,b) = 1,2                         # ERROR -- target must be in a list or tuple
*(a,b), = 1,2                        # a = 1, b = 2

*(a,b) = 'XY'                        # ERROR -- target must be in a list or tuple
*(a,b), = 'XY'                       # a = 'X', b = 'Y'

*(a, b) = 'this'                     # ERROR -- target must be in a list or tuple
*(a, b), = 'this'                    # ERROR -- too many values to unpack
*(a, *b), = 'this'                   # a = 't', b = ['h', 'i', 's']

*(a, *b), c = 'this'                 # a = 't', b = ['h', 'i'], c = 's'

*(a,*b), = 1,2,3,3,4,5,6,7           # a = 1, b = [2, 3, 3, 4, 5, 6, 7]

*(a,*b), *c = 1,2,3,3,4,5,6,7        # ERROR -- two starred expressions in assignment
*(a,*b), (*c,) = 1,2,3,3,4,5,6,7     # ERROR -- 'int' object is not iterable
*(a,*b), c = 1,2,3,3,4,5,6,7         # a = 1, b = [2, 3, 3, 4, 5, 6], c = 7
*(a,*b), (*c,) = 1,2,3,4,5,'XY'      # a = 1, b = [2, 3, 4, 5], c = ['X', 'Y']

*(a,*b), c, d = 1,2,3,3,4,5,6,7      # a = 1, b = [2, 3, 3, 4, 5], c = 6, d = 7
*(a,*b), (c, d) = 1,2,3,3,4,5,6,7    # ERROR -- 'int' object is not iterable
*(a,*b), (*c, d) = 1,2,3,3,4,5,6,7   # ERROR -- 'int' object is not iterable
*(a,*b), *(c, d) = 1,2,3,3,4,5,6,7   # ERROR -- two starred expressions in assignment


*(a,b), c = 'XY', 3                  # ERROR -- need more than 1 value to unpack
*(*a,b), c = 'XY', 3                 # a = [], b = 'XY', c = 3
(a,b), c = 'XY', 3                   # a = 'X', b = 'Y', c = 3

*(a,b), c = 'XY', 3, 4               # a = 'XY', b = 3, c = 4
*(*a,b), c = 'XY', 3, 4              # a = ['XY'], b = 3, c = 4
(a,b), c = 'XY', 3, 4                # ERROR -- too many values to unpack

วิธีการสรุปผลของการแสดงออกด้วยมืออย่างถูกต้อง?


28
จริงๆแล้วสิ่งเหล่านี้ส่วนใหญ่ซับซ้อนกว่าที่คุณเห็นในโค้ดทุกวัน เรียนรู้พื้นฐานของการเปิดรายการ / สิ่งที่บรรจุแล้วคุณจะสบายดี
Rafe Kettler

2
โปรดทราบว่าสิ่งเหล่านี้เป็นแบบวนซ้ำ ดังนั้นหากคุณขีดเส้นใต้คุณสามารถจัดการทุกอย่างได้ ลองแทนที่เช่น * (* a, b) ด้วย * x หาว่า x แกะกล่องอะไรแล้วเสียบ (* a, b) กลับเข้าไปใน x เป็นต้น
Peteris

4
@greengit ฉันคิดว่าตัวเองมีความรู้ขั้นสูงเกี่ยวกับ Python และฉันเพิ่งรู้กฎทั่วไป :) คุณไม่จำเป็นต้องรู้ทุกกรณีบางครั้งคุณก็ต้องยิงล่ามและทดสอบบางอย่าง
Rafe Kettler

ว้าวรายการที่ดี ฉันไม่รู้เกี่ยวกับa, *b = 1, 2, 3ประเภทของการแกะกล่อง แต่นี่คือ Py3k ใช่ไหม
Niklas R

คำตอบ:


113

ขออภัยสำหรับความยาวของโพสต์นี้ แต่ฉันตัดสินใจเลือกเพื่อความสมบูรณ์

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

สำหรับวัตถุประสงค์ในการแกะกล่องเท่านั้นการแทนที่ต่อไปนี้ใช้ได้ทางด้านขวาของ=(เช่นค่าrvalues ):

'XY' -> ('X', 'Y')
['X', 'Y'] -> ('X', 'Y')

หากคุณพบว่าค่าไม่ได้รับการคลายแพ็กคุณจะเลิกทำการแทนที่ (ดูด้านล่างสำหรับคำอธิบายเพิ่มเติม)

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

'X', 'Y' -> ('X', 'Y')
a, b -> (a, b)

เมื่อคำนึงถึงกฎง่ายๆเหล่านี้นี่คือตัวอย่างบางส่วน:

(a,b), c = "XY", "Z"                 # a = 'X', b = 'Y', c = 'Z'

ใช้กฎข้างต้นแปลง"XY"เป็น('X', 'Y')และครอบคลุมเครื่องหมายจุลภาคเปล่าใน parens:

((a, b), c) = (('X', 'Y'), 'Z')

การโต้ตอบด้วยภาพที่นี่ทำให้เห็นได้ชัดเจนว่างานมอบหมายอย่างไร

นี่คือตัวอย่างที่ผิดพลาด:

(a,b), c = "XYZ"

ตามกฎการเปลี่ยนตัวด้านบนเราจะได้รับสิ่งต่อไปนี้:

((a, b), c) = ('X', 'Y', 'Z')

นี่เป็นความผิดพลาดอย่างชัดเจน โครงสร้างที่ซ้อนกันไม่ตรงกัน ตอนนี้เรามาดูวิธีการทำงานของตัวอย่างที่ซับซ้อนขึ้นเล็กน้อย:

(a,b), c, = [1,2],'this'             # a = '1', b = '2', c = 'this'

ใช้กฎข้างต้นเราจะได้รับ

((a, b), c) = ((1, 2), ('t', 'h', 'i', 's'))

แต่ตอนนี้มันเป็นที่ชัดเจนจากโครงสร้างที่'this'จะไม่แตก cแต่ได้รับมอบหมายโดยตรงกับ ดังนั้นเราจึงยกเลิกการเปลี่ยนตัว

((a, b), c) = ((1, 2), 'this')

ตอนนี้เรามาดูกันว่าจะเกิดอะไรขึ้นเมื่อเรารวมcทูเพิล:

(a,b), (c,) = [1,2],'this'           # ERROR -- too many values to unpack

กลายเป็น

((a, b), (c,)) = ((1, 2), ('t', 'h', 'i', 's'))

อีกครั้งข้อผิดพลาดนั้นชัดเจน cจะไม่เป็นตัวแปรเปลือยกาย (c,)แต่ตัวแปรภายในลำดับและเพื่อลำดับที่สอดคล้องกันทางด้านขวาจะเป็นจำนวนมาก แต่ลำดับมีความยาวต่างกันจึงเกิดข้อผิดพลาด

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

a, *b, c = "X...Y"                   # a = 'X', b = ['.','.','.'], c = 'Y'

สิ่งนี้จะกลายเป็น

(a, *b, c) = ('X', '.', '.', '.', 'Y')

วิธีที่ง่ายที่สุดในการวิเคราะห์สิ่งนี้คือการทำงานจากจุดสิ้นสุด 'X'ได้รับมอบหมายให้aและได้รับมอบหมายให้'Y' ค่าที่เหลืออยู่ในลำดับที่จะใส่ในรายการและมอบหมายให้cb

ค่าที่เหมือน(*a, b)และ(a, *b)เป็นเพียงกรณีพิเศษข้างต้น คุณไม่สามารถมี*ตัวดำเนินการสองตัวในลำดับ lvalue เดียวได้เพราะมันจะคลุมเครือ ค่านิยมจะไปอยู่ที่ไหน(a, *b, *c, d)- ในbหรือc? เดี๋ยวจะพิจารณากรณีที่ซ้อนกัน

*a = 1                               # ERROR -- target must be in a list or tuple

ข้อผิดพลาดนี้ค่อนข้างอธิบายได้ด้วยตนเอง เป้าหมาย ( *a) ต้องอยู่ในทูเพิล

*a, = (1,2)                          # a = [1,2]

วิธีนี้ใช้ได้ผลเพราะมีเครื่องหมายจุลภาคเปล่า การใช้กฎ ...

(*a,) = (1, 2)

เนื่องจากไม่มีตัวแปรอื่นใดนอกเหนือจาก*aนั้นให้ทำการสรุป*aค่าทั้งหมดในลำดับ rvalue จะเกิดอะไรขึ้นถ้าคุณแทนที่(1, 2)ด้วยค่าเดียว?

*a, = 1                              # ERROR -- 'int' object is not iterable

กลายเป็น

(*a,) = 1

อีกครั้งข้อผิดพลาดที่นี่สามารถอธิบายได้ด้วยตนเอง คุณไม่สามารถแกะสิ่งที่ไม่ใช่ลำดับและ*aต้องการบางอย่างเพื่อแกะกล่อง เราจึงนำมาเรียงตามลำดับ

*a, = [1]                            # a = [1]

ซึ่งเทียบเท่ากับ

(*a,) = (1,)

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

*a, = (1)                            # ERROR -- 'int' object is not 

ตอนนี้สำหรับการทำรัง จริงๆแล้วตัวอย่างนี้ไม่ได้อยู่ในส่วน "NESTED" ของคุณ บางทีคุณอาจไม่รู้ว่ามันซ้อนกันอยู่?

(a,b), *c = 'XY', 2, 3               # a = 'X', b = 'Y', c = [2,3]

กลายเป็น

((a, b), *c) = (('X', 'Y'), 2, 3)

ค่าแรกในทูเปิลระดับบนสุดจะได้รับมอบหมายและค่าที่เหลือในทูเปิลระดับบนสุด ( 2และ3) จะถูกกำหนดให้c- ตามที่เราควรจะคาดหวัง

(a,b),c = 1,2,3                      # ERROR -- too many values to unpack
*(a,b), c = 1,2,3                    # a = 1, b = 2, c = 3

ฉันได้อธิบายไปแล้วข้างต้นว่าทำไมบรรทัดแรกจึงแสดงข้อผิดพลาด บรรทัดที่สองโง่ แต่นี่คือเหตุผลว่าทำไมมันถึงได้ผล:

(*(a, b), c) = (1, 2, 3)

ตามที่อธิบายไว้ก่อนหน้านี้เราทำงานจากจุดสิ้นสุด 3ถูกกำหนดให้cจากนั้นค่าที่เหลือจะถูกกำหนดให้กับตัวแปรโดยมีค่า*ก่อนหน้านี้ในกรณี(a, b)นี้ นั่นเท่ากับ(a, b) = (1, 2)ซึ่งเกิดขึ้นได้เพราะมีองค์ประกอบจำนวนที่เหมาะสม ฉันไม่คิดว่าจะมีเหตุผลอะไรที่จะปรากฏในรหัสการทำงาน ในทำนองเดียวกัน

*(a, *b), c = 'this'                 # a = 't', b = ['h', 'i'], c = 's'

กลายเป็น

(*(a, *b), c) = ('t', 'h', 'i', 's')

ทำงานจากปลาย's'ได้รับมอบหมายให้cและได้รับมอบหมายให้('t', 'h', 'i') (a, *b)ทำงานอีกครั้งจากจุดสิ้นสุด't'ถูกกำหนดให้aและ('h', 'i')ถูกกำหนดให้ b เป็นรายการ นี่เป็นอีกตัวอย่างโง่ ๆ ที่ไม่ควรปรากฏในโค้ดการทำงาน


24
เนื่องจาก OP ให้ตัวอย่างรายการที่ยาวคุณจึงควรให้คำอธิบายแบบยาว ๆ
John Y

7

ฉันพบว่า Python 2 tuple คลายออกค่อนข้างตรงไปตรงมา ชื่อแต่ละชื่อทางด้านซ้ายจะสอดคล้องกับลำดับทั้งหมดหรือรายการเดียวในลำดับทางด้านขวา หากชื่อตรงกับรายการเดียวของลำดับใด ๆ ก็ต้องมีชื่อเพียงพอที่จะครอบคลุมรายการทั้งหมด

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

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

เพียงเพราะคุณสามารถเขียนนิพจน์ที่ซับซ้อนตามอำเภอใจไม่ได้หมายความว่าคุณควร คุณสามารถเขียนรหัสเหมือนmap(map, iterable_of_transformations, map(map, iterable_of_transformations, iterable_of_iterables_of_iterables))แต่คุณทำไม่ได้


หมายเหตุ: ฉันเขียนโค้ดแบบนั้นยกเว้นหลายระดับที่ซับซ้อนกว่านี้ มันมีจุดมุ่งหมายเพื่อเป็นการออกกำลังกายเท่านั้นและทำด้วยความรู้อย่างเต็มที่ว่าหลังจากสามเดือนมันจะไม่มีความหมายสำหรับฉันและจะไม่มีใครเข้าใจได้อีก ถ้าจำไม่ผิดมันใช้จุดในการทดสอบรูปหลายเหลี่ยมแปลงพิกัดบางส่วนและสร้าง SVG, HTML และ JavaScript
agf

3

ฉันคิดว่ารหัสของคุณอาจทำให้เข้าใจผิดโดยใช้รูปแบบอื่นในการแสดง

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

ฉันชอบใช้การเปิดกล่องเฉพาะสำหรับงานง่ายๆเช่น swap

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