Python: พิมพ์นิพจน์ตัวสร้าง?


103

ใน Python shell ถ้าฉันป้อนข้อมูลความเข้าใจรายการเช่น:

>>> [x for x in string.letters if x in [y for y in "BigMan on campus"]]

ฉันได้รับผลการพิมพ์ที่สวยงาม:

['a', 'c', 'g', 'i', 'm', 'n', 'o', 'p', 's', 'u', 'B', 'M']

เหมือนกันสำหรับความเข้าใจในพจนานุกรม:

>>> {x:x*2 for x in range(1,10)}
{1: 2, 2: 4, 3: 6, 4: 8, 5: 10, 6: 12, 7: 14, 8: 16, 9: 18}

หากฉันป้อนนิพจน์ตัวสร้างฉันไม่ได้รับคำตอบที่เป็นมิตรเช่นนี้:

>>> (x for x in string.letters if x in (y for y in "BigMan on campus"))
<generator object <genexpr> at 0x1004a0be0>

ฉันรู้ว่าฉันทำได้:

>>> for i in _: print i,
a c g i m n o p s u B M

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


2
ปัญหาที่แท้จริงที่นี่คืออะไร? คุณขาดอะไร?
Andreas Jung

3
@pynator: "ปัญหาที่แท้จริง" คือฉันต้องการที่จะสามารถพิมพ์เนื้อหาในgenerator objectขณะที่ฉันสร้างความเข้าใจแบบโต้ตอบได้ที่ข้อความโต้ตอบแบบโต้ตอบ โทรlist(_)ไม่ได้ สิ่งที่ฉันได้ทำคือการใช้รายการความเข้าใจจากนั้นเปลี่ยนสิ่งเหล่านั้นให้เป็น genexp ในโค้ดที่ใหญ่ขึ้น สิ่งเหล่านี้อาจล้มเหลวในขณะดำเนินการในลักษณะที่ไม่เข้าใจในรายการ
the wolf

5
คำตอบสั้น ๆ คือไม่สามารถพิมพ์นิพจน์เครื่องกำเนิดไฟฟ้าได้เนื่องจากไม่มีค่า สร้างขึ้นตามความต้องการ สิ่งที่คุณสามารถทำได้ (สมมติว่าเครื่องกำเนิดไฟฟ้าหยุดทำงานในบางครั้ง) คือเอาค่าทั้งหมดออกจากมันเช่นเดียวกับlist()แล้วพิมพ์ออกมา
Kos

คำตอบ:


163

คำตอบที่รวดเร็ว:

การทำlist()รอบ ๆ นิพจน์เครื่องกำเนิดไฟฟ้านั้น (เกือบ) เทียบเท่ากับการมี []วงเล็บล้อมรอบ ใช่คุณสามารถทำได้

>>> list((x for x in string.letters if x in (y for y in "BigMan on campus")))

แต่คุณก็ทำได้เช่นกัน

>>> [x for x in string.letters if x in (y for y in "BigMan on campus")]

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

คำอธิบายโดยละเอียด:

นิพจน์กำเนิดเป็น "เปล่า" forการแสดงออก ชอบมาก:

x*x for x in range(10)

ตอนนี้คุณไม่สามารถยึดติดกับมันได้ด้วยตัวเองคุณจะได้รับข้อผิดพลาดทางไวยากรณ์ แต่ใส่วงเล็บไว้รอบ ๆ ก็ได้

>>> (x*x for x in range(10))
<generator object <genexpr> at 0xb7485464>

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

>>> sorted(x*x for x in range(10))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

โดยพื้นฐานแล้วความเข้าใจอื่น ๆ ทั้งหมดที่มีอยู่ใน Python 3 และ Python 2.7 นั้นเป็นเพียงแค่น้ำตาลวากยสัมพันธ์รอบ ๆ นิพจน์ของเครื่องกำเนิดไฟฟ้า ตั้งค่าความเข้าใจ:

>>> {x*x for x in range(10)}
{0, 1, 4, 81, 64, 9, 16, 49, 25, 36}

>>> set(x*x for x in range(10))
{0, 1, 4, 81, 64, 9, 16, 49, 25, 36}

ความเข้าใจ Dict:

>>> dict((x, x*x) for x in range(10))
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

>>> {x: x*x for x in range(10)}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

และแสดงรายการความเข้าใจภายใต้ Python 3:

>>> list(x*x for x in range(10))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

>>> [x*x for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

ภายใต้ Python 2 ความเข้าใจในรายการไม่ได้เป็นเพียงแค่น้ำตาลวากยสัมพันธ์เท่านั้น แต่ข้อแตกต่างเพียงอย่างเดียวคือ x จะอยู่ภายใต้ Python 2 รั่วไหลเข้าสู่เนมสเปซ

>>> x
9

ในขณะที่อยู่ภายใต้ Python 3 คุณจะได้รับ

>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

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

>>> foo = (x*x for x in range(10))
>>> [foo]
[<generator object <genexpr> at 0xb7559504>]

ในกรณีนี้คุณจะต้องโทรlist():

>>> list(foo)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

แม้ว่าจะได้ผล แต่ก็ค่อนข้างโง่:

>>> [x for x in foo]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

5
คำที่เป็นทางการยังคงเป็น "generator expression" เนื่องจากคำว่า "comp understandion" หมายถึงการวนซ้ำซึ่งเป็นสิ่งหนึ่งที่ genexp ไม่ทำเนื่องจากคำถามและคำตอบนี้แสดงให้เห็นอย่างชัดเจน :)
ncoghlan

2
list( generator-expression )ไม่ได้พิมพ์นิพจน์ของเครื่องกำเนิดไฟฟ้า มันกำลังสร้างรายการ (แล้วพิมพ์ในเชลล์แบบโต้ตอบ) แทนที่จะสร้างรายการใน Python 3 คุณสามารถแยกนิพจน์ตัวสร้างเป็นคำสั่งพิมพ์ได้ อิอิ) print(*(generator-expression)). สิ่งนี้จะพิมพ์องค์ประกอบโดยไม่มีเครื่องหมายจุลภาคและไม่มีวงเล็บที่จุดเริ่มต้นและจุดสิ้นสุด
AJNeufeld

18

ไม่เหมือนกับรายการหรือพจนานุกรมเครื่องกำเนิดไฟฟ้าอาจไม่มีที่สิ้นสุด การทำเช่นนี้จะไม่ได้ผล:

def gen():
    x = 0
    while True:
        yield x
        x += 1
g1 = gen()
list(g1)   # never ends

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

g1 = gen()
[g1.next() for i in range(10)]

2
โหวตขึ้นเนื่องจากข้อความที่ว่าเครื่องกำเนิดไฟฟ้าสามารถไม่มีที่สิ้นสุดได้ดังนั้นจึงทำให้เกิดการวนซ้ำหรือหยุดทั้งหมด (ขึ้นอยู่กับข้อกำหนดของคุณ (ฮ่า ๆ ))
Milan Velebit

ใช้[next(g1) for i in range(10)]ใน Python 3
Deepank

16

หรือคุณสามารถทำmapซ้ำได้ตลอดเวลาโดยไม่จำเป็นต้องสร้างรายการกลาง:

>>> _ = map(sys.stdout.write, (x for x in string.letters if x in (y for y in "BigMan on campus")))
acgimnopsuBM

3
นี่เป็นคำตอบเดียวที่พิมพ์เนื้อหาของเครื่องกำเนิดไฟฟ้าโดยไม่ต้องสร้างวัตถุขนาดใหญ่
Marek R

16

คุณสามารถรวมนิพจน์ในการเรียกร้องไปที่list:

>>> list(x for x in string.letters if x in (y for y in "BigMan on campus"))
['a', 'c', 'g', 'i', 'm', 'n', 'o', 'p', 's', 'u', 'B', 'M']

2
>>> list(x for x in string.letters if x in (y for y in "BigMan on campus"))
['a', 'c', 'g', 'i', 'm', 'n', 'o', 'p', 's', 'u', 'B', 'M']

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