ทำไมไม่มีความเข้าใจสิ่งอันดับใน Python


340

อย่างที่เราทุกคนรู้กันดีว่ามีความเข้าใจในรายการเช่นกัน

[i for i in [1, 2, 3, 4]]

และมีความเข้าใจในพจนานุกรมเช่น

{i:j for i, j in {1: 'a', 2: 'b'}.items()}

แต่

(i for i in (1, 2, 3))

จะจบลงในเครื่องกำเนิดไฟฟ้าไม่ใช่ความtupleเข้าใจ ทำไมถึงเป็นอย่างนั้น?

ฉันเดาว่ามันเป็นtupleสิ่งที่ไม่เปลี่ยนรูป แต่ดูเหมือนจะไม่เป็นคำตอบ


15
นอกจากนี้ยังมีชุดความเข้าใจ - ซึ่งดูเหมือนเป็นความเข้าใจแบบ dict ...
mgilson

3
มีข้อผิดพลาดทางไวยากรณ์ในรหัสของคุณ: {i:j for i,j in {1:'a', 2:'b'}}ควรเป็น{i:j for i,j in {1:'a', 2:'b'}.items()}
Inbar Rose

@InbarRose ขอบคุณที่ชี้ให้เห็น -.
Shady Xu

เพียงเพื่อประโยชน์ของลูกหลานมีการสนทนาเกี่ยวกับสิ่งนี้เกิดขึ้นในการแชท Python
Inbar Rose

เห็นได้ชัดว่ามี stackoverflow.com/a/51811147/9627166
Super S

คำตอบ:


471

คุณสามารถใช้นิพจน์ตัวสร้าง:

tuple(i for i in (1, 2, 3))

แต่มีการใส่วงเล็บไว้ใน…นิพจน์ตัวสร้าง


15
จากข้อโต้แย้งนี้เราสามารถพูดได้ว่า list-comprehension นั้นไม่จำเป็นเช่นกัน: list(i for i in (1,2,3)). ฉันคิดว่ามันเป็นเพียงเพราะไม่มีไวยากรณ์ที่สะอาดสำหรับมัน (หรืออย่างน้อยก็ไม่มีใครคิดได้)
mgilson

79
รายการหรือชุดหรือความเข้าใจแบบ dict เป็นเพียงน้ำตาลประโยคเพื่อใช้นิพจน์เครื่องกำเนิดไฟฟ้าที่แสดงผลชนิดเฉพาะ list(i for i in (1, 2, 3))เป็นนิพจน์ตัวสร้างที่ส่งออกรายการset(i for i in (1, 2, 3))เป็นชุดผลลัพธ์ นั่นหมายความว่าไม่จำเป็นต้องใช้ไวยากรณ์ความเข้าใจใช่หรือไม่ อาจจะไม่ใช่ แต่มันมีประโยชน์มาก สำหรับกรณีที่หายากที่คุณต้องการ tuple แทนนิพจน์ตัวสร้างจะทำชัดเจนและไม่ต้องใช้วงเล็บปีกกาหรือวงเล็บอื่น
Martijn Pieters

16
คำตอบคือเพราะเห็นได้ชัดว่าไวยากรณ์ tuple และวงเล็บไม่ชัดเจน
ชาร์ลส์ซัลเวีย

19
ความแตกต่างระหว่างการใช้ความเข้าใจและการใช้ตัวสร้าง + ตัวสร้างนั้นเป็นเรื่องที่ละเอียดอ่อนกว่าหากคุณใส่ใจกับประสิทธิภาพ ความเข้าใจส่งผลให้การก่อสร้างเร็วขึ้นเมื่อเทียบกับการใช้เครื่องกำเนิดไฟฟ้าที่ส่งไปยังตัวสร้าง ในกรณีหลังคุณกำลังสร้างและดำเนินการฟังก์ชั่นและฟังก์ชั่นมีราคาแพงใน Python [thing for thing in things]สร้างรายการเร็วกว่าlist(thing for thing in things)มาก ความเข้าใจแบบ tuple จะไม่ไร้ประโยชน์ tuple(thing for thing in things)มีปัญหาความล่าช้าและtuple([thing for thing in things])อาจมีปัญหาหน่วยความจำ
Justin Turner Arthur

9
@MartijnPieters คุณสามารถป้อนข้อความซ้ำได้A list or set or dict comprehension is just syntactic sugar to use a generator expressionไหม? มันทำให้เกิดความสับสนโดยผู้คนมองว่าสิ่งเหล่านี้มีความหมายเทียบเท่า มันไม่ได้เป็นน้ำตาลทรายทางเทคนิคเนื่องจากกระบวนการแตกต่างกันจริงแม้ว่าผลิตภัณฑ์สุดท้ายจะเหมือนกัน
jpp

77

Raymond Hettinger (หนึ่งในผู้พัฒนาหลัก Python) ได้กล่าวถึง tuples ในทวีตล่าสุด :

#python tip: โดยทั่วไปรายการจะใช้สำหรับการวนซ้ำ; tuples สำหรับ structs รายการเป็นเนื้อเดียวกัน ทูเปิลที่ต่างกัน รายการความยาวผันแปร

(สำหรับฉัน) นี้สนับสนุนแนวคิดที่ว่าหากรายการในลำดับเกี่ยวข้องกันมากพอที่จะสร้างโดย a, well, generator แล้วควรเป็นรายการ แม้ว่า tuple สามารถทำซ้ำได้และดูเหมือนว่าเป็นรายการที่ไม่เปลี่ยนรูปแบบ แต่เป็น Python ที่เทียบเท่ากับ C struct

struct {
    int a;
    char b;
    float c;
} foo;

struct foo x = { 3, 'g', 5.9 };

กลายเป็นงูหลาม

x = (3, 'g', 5.9)

26
คุณสมบัติที่ไม่สามารถเปลี่ยนแปลงได้มีความสำคัญและบ่อยครั้งเป็นเหตุผลที่ดีที่จะใช้สิ่งอันดับเมื่อคุณมักจะใช้รายการ ตัวอย่างเช่นหากคุณมีรายการหมายเลข 5 ตัวที่คุณต้องการใช้เป็นกุญแจสำคัญในการเขียน dict ดังนั้น tuple เป็นวิธีการที่จะไป
pavon

เป็นคำแนะนำที่ดีจาก Raymond Hettinger ฉันยังคงบอกว่ามีกรณีการใช้งานสำหรับการใช้ตัวสร้าง tuple กับเครื่องกำเนิดไฟฟ้าเช่นการคลายโครงสร้างอื่นอาจใหญ่ขึ้นเป็นกล่องเล็กโดยทำซ้ำกับพนักงานที่คุณสนใจที่จะแปลงเป็น tuple
dave

2
@dave คุณสามารถใช้operator.itemgetterในกรณีนี้ได้
chepner

@chepner ฉันเข้าใจแล้ว นั่นใกล้เคียงกับที่ฉันหมายถึงมาก มันจะคืนค่า callable ดังนั้นหากฉันต้องการทำเพียงครั้งเดียวเมื่อฉันไม่เห็นการชนะมากเพียงแค่ใช้tuple(obj[item] for item in items)โดยตรง ในกรณีของฉันฉันฝังสิ่งนี้ลงในรายการความเข้าใจเพื่อสร้างรายการของสิ่งอันดับ ถ้าฉันต้องทำมันซ้ำ ๆ ตลอดทั้งรหัสแล้ว itemgetter ดูดีมาก บางทีนักคิดเรื่องไอเท็มจะเป็นคนที่งี่เง่ากว่ากัน
dave

ฉันเห็นความสัมพันธ์ระหว่าง frozenset และตั้งคล้ายคลึงกับของ tuple และรายการ มันเป็นเรื่องของความหลากหลายน้อยลงและความผันแปรไม่ได้ - frozensets และ tuples สามารถเป็นกุญแจสำคัญในพจนานุกรมรายการและเซตต่างๆไม่สามารถทำได้เนื่องจากความไม่แน่นอน
พูดได้หลายภาษา

56

ตั้งแต่ Python 3.5คุณสามารถใช้*การคลายเครื่องหมายสแลทเพื่อแยกแพ็กเกจ expresion:

*(x for x in range(10)),

2
นี่เป็นสิ่งที่ยอดเยี่ยม (และใช้งานได้) แต่ฉันไม่สามารถหาที่ใดก็ได้ที่มีเอกสาร! คุณมีลิงค์หรือไม่?
felixphew

8
หมายเหตุ: ในฐานะที่เป็นรายละเอียดการนำไปปฏิบัติสิ่งนี้จะเหมือนกับการทำtuple(list(x for x in range(10)))(พา ธของรหัสเหมือนกันโดยที่ทั้งคู่จะสร้าง a listโดยมีความแตกต่างเพียงอย่างเดียวคือขั้นตอนสุดท้ายคือการสร้างtupleจากlistและทิ้งlistเมื่อtupleเอาต์พุต ต้องระบุ). หมายความว่าคุณไม่ได้หลีกเลี่ยงคู่ชั่วคราว
ShadowRanger

4
หากต้องการขยายความคิดเห็นของ @ShadowRanger ต่อไปนี้เป็นคำถามที่พวกเขาแสดงให้เห็นว่าไวยากรณ์ตัวอักษร splat + tuple แท้จริงแล้วค่อนข้างช้ากว่าการส่งนิพจน์ตัวกำเนิดไปยังตัวสร้าง tuple
Lucubrator

ฉันกำลังลองสิ่งนี้ใน Python 3.7.3 และ*(x for x in range(10))ไม่ทำงาน SyntaxError: can't use starred expression hereฉันได้รับ อย่างไรก็ตามการtuple(x for x in range(10))ทำงาน
Ryan H.

4
@RyanH คุณต้องใส่เครื่องหมายจุลภาคในที่สุด
czheo

27

เป็นโปสเตอร์อื่นmacmกล่าวถึงวิธีที่เร็วที่สุดในการสร้าง tuple tuple([generator])จากเครื่องกำเนิดไฟฟ้า


การเปรียบเทียบประสิทธิภาพ

  • รายการความเข้าใจ:

    $ python3 -m timeit "a = [i for i in range(1000)]"
    10000 loops, best of 3: 27.4 usec per loop
  • Tuple จากรายการความเข้าใจ:

    $ python3 -m timeit "a = tuple([i for i in range(1000)])"
    10000 loops, best of 3: 30.2 usec per loop
  • Tuple จากเครื่องกำเนิดไฟฟ้า:

    $ python3 -m timeit "a = tuple(i for i in range(1000))"
    10000 loops, best of 3: 50.4 usec per loop
  • Tuple จากการเปิดออก:

    $ python3 -m timeit "a = *(i for i in range(1000)),"
    10000 loops, best of 3: 52.7 usec per loop

หลามเวอร์ชั่นของฉัน :

$ python3 --version
Python 3.6.3

ดังนั้นคุณควรสร้างสิ่งอันดับจากรายการความเข้าใจเว้นแต่ว่าประสิทธิภาพจะไม่เป็นปัญหา


10
หมายเหตุ: tupleของ listcomp ต้องใช้หน่วยความจำสูงสุดขึ้นอยู่กับขนาดรวมของรอบสุดท้ายและtuple ของ genexpr ในขณะที่ช้าลงหมายความว่าคุณจ่ายเพียงค่าสุดท้ายเท่านั้นไม่ใช่ชั่วคราว(genexpr เองจะครอบครองหน่วยความจำที่มีการเปลี่ยนแปลงอย่างคร่าวๆ) มักจะไม่ได้มีความหมาย แต่อาจสำคัญเมื่อขนาดที่เกี่ยวข้องมีขนาดใหญ่ listtupletuplelist
ShadowRanger

25

ความเข้าใจทำงานโดยการวนซ้ำหรือวนซ้ำรายการและกำหนดลงในคอนเทนเนอร์ Tuple ไม่สามารถรับการมอบหมายได้

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

นอกจากนี้ - tuple มี Constructor ของตัวเองtuple()ซึ่งคุณสามารถให้ตัววนซ้ำได้ ซึ่งหมายความว่าการสร้างสิ่งอันดับคุณสามารถทำได้:

tuple(i for i in (1,2,3))

9
ในบางวิธีฉันเห็นด้วย (ไม่จำเป็นเพราะรายชื่อจะทำ) แต่ด้วยวิธีอื่นฉันไม่เห็นด้วย (เกี่ยวกับเหตุผลที่เป็นเพราะไม่เปลี่ยนรูป) ในบางวิธีมันสมเหตุสมผลกว่าที่จะเข้าใจวัตถุที่ไม่เปลี่ยนรูป ที่ไม่lst = [x for x in ...]; x.append()?
mgilson

@ mgilson ฉันไม่แน่ใจว่าเกี่ยวข้องกับสิ่งที่ฉันพูดได้อย่างไร
Inbar Rose

2
@ mgilson หาก tuple ไม่เปลี่ยนรูปนั่นหมายความว่าการใช้งานพื้นฐานไม่สามารถ "สร้าง" tuple ("generation" ซึ่งหมายถึงการสร้างทีละชิ้น) หมายความว่าไม่เปลี่ยนรูปคุณไม่สามารถสร้างชิ้นที่มี 4 ชิ้นด้วยการเปลี่ยนชิ้นที่มี 3 ชิ้น คุณใช้ tuple "generation" แทนโดยสร้างรายการสิ่งที่ออกแบบมาเพื่อสร้างจากนั้นสร้าง tuple เป็นขั้นตอนสุดท้ายและทิ้งรายการ ภาษาสะท้อนความเป็นจริงนี้ คิดว่า tuples เป็น C structs
สกอตต์

2
แม้ว่ามันจะสมเหตุสมผลสำหรับน้ำตาล syntactic ของความเข้าใจในการทำงานสำหรับ tuples เนื่องจากคุณไม่สามารถใช้ tuple จนกว่าความเข้าใจจะถูกส่งกลับ อย่างมีประสิทธิภาพมันไม่ได้ทำหน้าที่เหมือนที่ไม่แน่นอน แต่ความเข้าใจแบบ tuple อาจทำตัวเหมือนการต่อท้ายสตริง
uchuugaka

12

การเดาที่ดีที่สุดของฉันคือพวกเขาหมดวงเล็บและไม่คิดว่ามันจะมีประโยชน์มากพอที่จะเพิ่มไวยากรณ์ "น่าเกลียด" ...


1
ไม่ได้ใช้วงเล็บมุม
uchuugaka

@uchuugaka - ไม่สมบูรณ์ พวกเขากำลังใช้สำหรับผู้ประกอบการเปรียบเทียบ มันอาจจะยังคงสามารถทำได้โดยไม่ต้องมีความกำกวม แต่อาจจะไม่คุ้มค่ากับความพยายาม ...
mgilson

3
@uchuugaka น่าสังเกตว่า{*()}แม้ว่าน่าเกลียดทำงานเป็นชุดตัวอักษรที่ว่างเปล่า!
MI Wright

1
ฮึ. จากมุมมองเกี่ยวกับสุนทรียศาสตร์ฉันคิดว่าฉันเป็นส่วนหนึ่งของset():)
mgilson

1
@QuantumMechanic: ใช่นั่นคือประเด็น; การทำให้แตกออกโดยทั่วไปทำให้ว่างเปล่า "ตั้งตัวอักษร" ที่เป็นไปได้ โปรดทราบว่า{*[]}ด้อยกว่าตัวเลือกอื่นอย่างเคร่งครัด สตริงที่ว่างเปล่าและว่างเปล่าtupleถูกเปลี่ยนรูปเป็น singletons setจึงไม่มีชั่วคราวเป็นสิ่งจำเป็นในการสร้างที่ว่างเปล่า ในทางตรงกันข้ามความว่างเปล่าlistไม่ใช่แบบซิงเกิลดังนั้นคุณต้องสร้างมันขึ้นมาใช้สร้างsetมันแล้วทำลายมันเสียประสิทธิภาพการทำงานที่ไม่สำคัญใด ๆ ที่ผู้ดำเนินการลิงตาเดียวให้มา
ShadowRanger

8

Tuples ไม่สามารถผนวกท้ายเหมือนรายการได้อย่างมีประสิทธิภาพ

ดังนั้นความเข้าใจ tuple จะต้องใช้รายการภายในแล้วแปลงเป็น tuple

นั่นจะเหมือนกับสิ่งที่คุณทำในตอนนี้: tuple ([comprehension])


3

วงเล็บไม่สร้างสิ่งอันดับ aka หนึ่ง = (สอง) ไม่ใช่ tuple วิธีเดียวที่อยู่รอบตัวคือหนึ่ง = (สอง,) หรือหนึ่ง = tuple (สอง) ดังนั้นทางออกคือ:

tuple(i for i in myothertupleorlistordict) 

ดี มันเกือบจะเหมือนกัน
uchuugaka

-1

ฉันเชื่อว่ามันเป็นเพียงเพื่อความชัดเจนเราไม่ต้องการที่จะถ่วงภาษาด้วยสัญลักษณ์ที่แตกต่างกันมากเกินไป นอกจากนี้ยังtupleไม่จำเป็นต้องมีความเข้าใจรายการสามารถใช้แทนด้วยความแตกต่างความเร็วเล็กน้อยซึ่งแตกต่างจาก dict comprehension เมื่อเทียบกับ list comprehension


-2

เราสามารถสร้างสิ่งอันดับจากรายการความเข้าใจ หนึ่งต่อไปนี้เพิ่มตัวเลขสองตัวตามลำดับลงใน tuple และให้รายการจากตัวเลข 0-9

>>> print k
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
>>> r= [tuple(k[i:i+2]) for i in xrange(10) if not i%2]
>>> print r
[(0, 1), (2, 3), (4, 5), (6, 7), (8, 9)]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.