ในทางปฏิบัติแล้วอะไรคือการใช้หลักของซินแท็กซ์ใหม่ "yield from" ใน Python 3.3


407

ฉันมีช่วงเวลาที่ยากห่อสมองของฉันรอบPEP 380

  1. สถานการณ์ที่ "ผลผลิตจาก" มีประโยชน์คืออะไร
  2. กรณีการใช้คลาสสิกคืออะไร?
  3. ทำไมจึงเปรียบเทียบกับไมโครกระทู้

[อัพเดท]

ตอนนี้ฉันเข้าใจสาเหตุของปัญหาของฉัน ฉันใช้เครื่องกำเนิดไฟฟ้า แต่ไม่เคยใช้ coroutines (แนะนำโดยPEP-342 ) แม้จะมีความคล้ายคลึงกันเครื่องกำเนิดและ coroutines เป็นสองแนวคิดที่แตกต่างกัน การทำความเข้าใจ coroutines (ไม่เพียงกำเนิด) เป็นกุญแจสำคัญในการทำความเข้าใจไวยากรณ์ใหม่

IMHO coroutines เป็นคุณสมบัติของ Python ที่คลุมเครือที่สุดหนังสือส่วนใหญ่ทำให้ดูไร้ประโยชน์และไม่น่าสนใจ

ขอบคุณสำหรับคำตอบที่ดี แต่ขอขอบคุณเป็นพิเศษเพื่อAGFและแสดงความคิดเห็นของเขาเชื่อมโยงกับการนำเสนอผลงานของดาวิดบีซ เดวิดหิน



7
วิดีโอการนำเสนอdabeaz.com/coroutinesของ David Beazley : youtube.com/watch?v=Z_OAlIhXziw
jcugat

คำตอบ:


570

ก่อนอื่นเรามาทำความเข้าใจก่อน คำอธิบายที่yield from gเทียบเท่าfor v in g: yield v ไม่ได้เริ่มก่อให้เกิดความยุติธรรมกับสิ่งที่yield fromเป็นอยู่ เพราะเรามาดูกันถ้าทุกอย่างyield fromนั้นขยายforลูปไปแล้วมันก็ไม่รับประกันว่าyield fromจะเพิ่มในภาษาและห้ามไม่ให้มีฟีเจอร์ใหม่ ๆ มากมายที่จะนำมาใช้ใน Python 2.x

สิ่งที่yield fromไม่สามารถจะสร้างการเชื่อมต่อแบบสองทิศทางโปร่งใสระหว่างโทรและย่อยกำเนิด :

  • การเชื่อมต่อนั้น "โปร่งใส" ในแง่ที่ว่ามันจะเผยแพร่ทุกอย่างถูกต้องด้วยเช่นกันไม่ใช่แค่องค์ประกอบที่สร้างขึ้น (เช่นมีการเผยแพร่ข้อยกเว้น)

  • การเชื่อมต่อคือ "สองทิศทาง" ในแง่ที่ว่าข้อมูลสามารถส่งได้ทั้งจากและไปยังเครื่องกำเนิด

( ถ้าเรากำลังพูดถึง TCP yield from gอาจหมายถึง "ตอนนี้ยกเลิกการเชื่อมต่อซ็อกเก็ตของลูกค้าของฉันชั่วคราวและเชื่อมต่อกับซ็อกเก็ตเซิร์ฟเวอร์อื่น" )

BTW หากคุณไม่แน่ใจว่าการส่งข้อมูลไปยังเครื่องกำเนิดไฟฟ้านั้นหมายความว่าอย่างไรคุณต้องทิ้งทุกอย่างและอ่านเกี่ยวกับcoroutinesก่อน - มันมีประโยชน์มาก (ตรงกันข้ามกับรูทีนย่อย ) แต่น่าเสียดายที่ไม่ค่อยรู้จักใน Python หลักสูตร Curious ของ Dave Beazley เกี่ยวกับ Coroutinesเป็นการเริ่มต้นที่ยอดเยี่ยม อ่านสไลด์ 24-33เพื่อไพรเมอร์ด่วน

การอ่านข้อมูลจากเครื่องกำเนิดไฟฟ้าโดยใช้ผลผลิตจาก

def reader():
    """A generator that fakes a read from a file, socket, etc."""
    for i in range(4):
        yield '<< %s' % i

def reader_wrapper(g):
    # Manually iterate over data produced by reader
    for v in g:
        yield v

wrap = reader_wrapper(reader())
for i in wrap:
    print(i)

# Result
<< 0
<< 1
<< 2
<< 3

แทนการทำซ้ำมากกว่าreader()เราก็สามารถyield fromมัน

def reader_wrapper(g):
    yield from g

ใช้งานได้และเราตัดรหัสหนึ่งบรรทัดออก และอาจเป็นเจตนาที่ชัดเจนเล็กน้อย (หรือไม่) แต่ไม่มีอะไรเปลี่ยนแปลงชีวิต

การส่งข้อมูลไปยังเครื่องกำเนิดไฟฟ้า (coroutine) โดยใช้อัตราผลตอบแทนจาก - ส่วนที่ 1

ตอนนี้ขอทำสิ่งที่น่าสนใจมากขึ้น ลองสร้าง coroutine ที่เรียกwriterว่ารับข้อมูลที่ส่งไปและเขียนลงใน socket, fd และอื่น ๆ

def writer():
    """A coroutine that writes data *sent* to it to fd, socket, etc."""
    while True:
        w = (yield)
        print('>> ', w)

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

def writer_wrapper(coro):
    # TBD
    pass

w = writer()
wrap = writer_wrapper(w)
wrap.send(None)  # "prime" the coroutine
for i in range(4):
    wrap.send(i)

# Expected result
>>  0
>>  1
>>  2
>>  3

wrapper ต้องยอมรับข้อมูลที่ถูกส่งไป (ชัด) และควรจัดการStopIterationเมื่อวง for หมดแล้ว เห็นได้ชัดว่าเพียงแค่ทำfor x in coro: yield xจะไม่ทำ นี่คือรุ่นที่ใช้งานได้

def writer_wrapper(coro):
    coro.send(None)  # prime the coro
    while True:
        try:
            x = (yield)  # Capture the value that's sent
            coro.send(x)  # and pass it to the writer
        except StopIteration:
            pass

หรือเราสามารถทำได้

def writer_wrapper(coro):
    yield from coro

ที่บันทึกรหัส 6 บรรทัดทำให้อ่านได้มากขึ้นและใช้งานได้ มายากล!

การส่งข้อมูลไปยังตัวสร้างผลตอบแทนจาก - ส่วนที่ 2 - การจัดการข้อยกเว้น

มาทำให้มันซับซ้อนขึ้น เกิดอะไรขึ้นถ้านักเขียนของเราต้องจัดการกับข้อยกเว้น? สมมติว่าwriterด้ามจับ a SpamExceptionและมันพิมพ์ออกมา***ถ้ามันเจอ

class SpamException(Exception):
    pass

def writer():
    while True:
        try:
            w = (yield)
        except SpamException:
            print('***')
        else:
            print('>> ', w)

ถ้าเราไม่เปลี่ยนwriter_wrapperล่ะ ใช้งานได้หรือไม่ มาลองกัน

# writer_wrapper same as above

w = writer()
wrap = writer_wrapper(w)
wrap.send(None)  # "prime" the coroutine
for i in [0, 1, 2, 'spam', 4]:
    if i == 'spam':
        wrap.throw(SpamException)
    else:
        wrap.send(i)

# Expected Result
>>  0
>>  1
>>  2
***
>>  4

# Actual Result
>>  0
>>  1
>>  2
Traceback (most recent call last):
  ... redacted ...
  File ... in writer_wrapper
    x = (yield)
__main__.SpamException

อืมมันไม่ทำงานเพราะx = (yield)เพียงยกข้อยกเว้นและทุกอย่างจะหยุดชะงัก ขอให้มันทำงาน แต่ตนเองจัดการข้อยกเว้นและส่งพวกเขาหรือการขว้างปาลงในเครื่องกำเนิดไฟฟ้าย่อย ( writer)

def writer_wrapper(coro):
    """Works. Manually catches exceptions and throws them"""
    coro.send(None)  # prime the coro
    while True:
        try:
            try:
                x = (yield)
            except Exception as e:   # This catches the SpamException
                coro.throw(e)
            else:
                coro.send(x)
        except StopIteration:
            pass

วิธีนี้ใช้ได้ผล

# Result
>>  0
>>  1
>>  2
***
>>  4

แต่ทำอย่างนี้!

def writer_wrapper(coro):
    yield from coro

yield fromจับโปร่งใสส่งค่าหรือการขว้างปาค่าลงย่อยกำเนิด

นี้ยังไม่ครอบคลุมทุกมุมกรณีแม้ว่า จะเกิดอะไรขึ้นถ้าเครื่องกำเนิดไฟฟ้าด้านนอกปิด? สิ่งที่เกี่ยวกับกรณีที่ sub-generator ส่งกลับค่า (ใช่ใน Python 3.3+, กำเนิดสามารถคืนค่า), วิธีการส่งกลับค่าควรจะเผยแพร่? ที่yield fromโปร่งใสจัดการทุกกรณีมุมเป็นที่น่าประทับใจจริงๆ yield fromเพียงใช้งานได้อย่างน่าอัศจรรย์และจัดการกับทุกกรณีเหล่านั้น

โดยส่วนตัวฉันรู้สึกว่าyield fromเป็นตัวเลือกคำหลักที่ไม่ดีเพราะมันไม่ได้ทำให้ลักษณะสองทางชัดเจน มีการเสนอคำหลักอื่น ๆ (เช่นdelegateแต่ถูกปฏิเสธเนื่องจากการเพิ่มคำหลักใหม่ในภาษานั้นยากกว่าการรวมคำที่มีอยู่เดิม

โดยสรุปเป็นวิธีที่ดีที่สุดที่จะคิดว่าyield fromเป็นตัวเรียกtransparent two way channelระหว่างและตัวสร้างย่อย

อ้างอิง:

  1. PEP 380 - ไวยากรณ์สำหรับการมอบหมายให้ผู้สร้างย่อย (Ewing) [v3.3, 2009-02-13]
  2. PEP 342 - Coroutines ผ่าน Enhanced Generators (GvR, Eby) [v2.5, 2005-05-10]

3
@PraveenGollakota ในส่วนที่สองของคำถามของคุณการส่งข้อมูลไปยังเครื่องกำเนิดไฟฟ้า (coroutine) โดยใช้อัตราผลตอบแทนจาก - ส่วนที่ 1 จะเกิดอะไรขึ้นถ้าคุณมีมากกว่า coroutines เพื่อส่งต่อรายการที่ได้รับไป? เช่นเดียวกับสถานการณ์ของผู้เผยแพร่หรือสมาชิกที่คุณมีหลาย coroutines ให้กับ wrapper ในตัวอย่างของคุณและรายการควรถูกส่งไปยังทั้งหมดหรือส่วนย่อยของพวกเขา?
Kevin Ghaboosi

3
@PraveenGollakota, รุ่งโรจน์สำหรับคำตอบที่ดี ตัวอย่างเล็ก ๆ ให้ฉันลองทำสิ่งใหม่ ๆ ลิงก์ไปยังหลักสูตร Dave Beazley เป็นโบนัส!
BiGYaN

1
ทำexcept StopIteration: passภายในwhile True:วงไม่ได้เป็นตัวแทนที่ถูกต้องของyield from coro- ซึ่งไม่ได้เป็นวงไม่สิ้นสุดและหลังจากcoroหมด (เช่นเพิ่ม StopIteration) writer_wrapperจะดำเนินการคำสั่งต่อไป หลังจากแถลงการณ์ครั้งล่าสุดมันจะเพิ่มระดับอัตโนมัติStopIterationเป็นผู้กำเนิดที่อ่อนล้า ...
Aprillion

1
... ดังนั้นหากwriterมีอยู่for _ in range(4)แทนที่จะwhile Trueพิมพ์หลังจาก>> 3นั้นมันก็จะเพิ่มอัตโนมัติStopIterationและสิ่งนี้จะได้รับการจัดการโดยอัตโนมัติyield fromและจากนั้นwriter_wrapperจะเพิ่มอัตโนมัติเป็นของตัวเองStopIterationและเนื่องจากwrap.send(i)ไม่ได้อยู่ในtryบล็อกมันจะถูกยกตอนนี้ ( คือการย้อนกลับเท่านั้นที่จะรายงานบรรทัดด้วยwrap.send(i)ไม่ใช่สิ่งใดจากภายในเครื่องกำเนิดไฟฟ้า)
Aprillion

3
เมื่ออ่าน " ไม่แม้แต่จะเริ่มทำเพื่อความยุติธรรม " ฉันรู้ว่าฉันได้รับคำตอบที่ถูกต้องแล้ว ขอบคุณสำหรับคำอธิบายที่ดี!
Hot.PxL

89

สถานการณ์ที่ "ผลผลิตจาก" มีประโยชน์คืออะไร

ทุกสถานการณ์ที่คุณมีวนรอบเช่นนี้:

for x in subgenerator:
  yield x

ดังที่ PEP อธิบายนี่เป็นความพยายามที่ไร้เดียงสาในการใช้ผู้สร้างย่อยมันขาดหลายแง่มุมโดยเฉพาะอย่างยิ่งการจัดการ.throw()/ .send()/ .close()กลไกที่แนะนำโดยPEP 342อย่างเหมาะสม ในการทำเช่นนี้อย่างถูกต้องจำเป็นต้องใช้รหัสที่ค่อนข้างซับซ้อน

กรณีการใช้คลาสสิกคืออะไร?

พิจารณาว่าคุณต้องการดึงข้อมูลจากโครงสร้างข้อมูลแบบเรียกซ้ำ สมมติว่าเราต้องการรับโหนดใบไม้ทั้งหมดในทรี:

def traverse_tree(node):
  if not node.children:
    yield node
  for child in node.children:
    yield from traverse_tree(child)

สิ่งสำคัญยิ่งกว่านั้นคือความจริงที่ว่าจนกระทั่งyield fromไม่มีวิธีการง่ายๆในการสร้างรหัสเครื่องกำเนิดไฟฟ้าใหม่ สมมติว่าคุณมีตัวสร้าง (ไม่มีสติ) เช่นนี้:

def get_list_values(lst):
  for item in lst:
    yield int(item)
  for item in lst:
    yield str(item)
  for item in lst:
    yield float(item)

ตอนนี้คุณตัดสินใจแยกลูปออกเป็นเครื่องกำเนิดไฟฟ้าแยกกัน โดยไม่ต้องyield fromนี่น่าเกลียดจนถึงจุดที่คุณจะคิดสองครั้งว่าคุณต้องการทำจริง ด้วยyield fromจริงๆแล้วมันเป็นเรื่องดีที่จะดู:

def get_list_values(lst):
  for sub in [get_list_values_as_int, 
              get_list_values_as_str, 
              get_list_values_as_float]:
    yield from sub(lst)

ทำไมจึงเปรียบเทียบกับไมโครกระทู้

ฉันคิดว่าส่วนนี้ใน PEPกำลังพูดถึงคือตัวสร้างทุกตัวมีบริบทการดำเนินการแยกของตัวเอง ประกอบกับข้อเท็จจริงที่ว่าการดำเนินการถูกสลับระหว่าง generator-iterator และผู้เรียกที่ใช้yieldและ__next__()ตามลำดับสิ่งนี้คล้ายกับเธรดซึ่งระบบปฏิบัติการจะสลับเธรดการเรียกใช้งานเป็นครั้งคราวพร้อมกับบริบทการดำเนินการ (stack, register, ... )

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

การเปรียบเทียบนั้นไม่ได้มีความเฉพาะเจาะจงyield fromแต่เป็นสมบัติทั่วไปของเครื่องกำเนิดไฟฟ้าใน Python


เครื่องกำเนิด Refactoring วันนี้เจ็บปวดมาก
Josh Lee

1
ฉันมักจะใช้ itertools เป็นจำนวนมากสำหรับการเปลี่ยนเครื่องกำเนิดไฟฟ้า (เช่น itertools.chain) ไม่ใช่เรื่องใหญ่อะไร ฉันชอบผลผลิตจาก แต่ฉันก็ยังล้มเหลวที่จะเห็นว่ามันคือการปฏิวัติ อาจเป็นเพราะ Guido คลั่งไคล้ แต่ฉันต้องคิดถึงภาพใหญ่ ฉันคิดว่ามันยอดเยี่ยมสำหรับการส่ง () เนื่องจากยากต่อการปรับโครงสร้าง แต่ฉันไม่ค่อยได้ใช้มันบ่อยนัก
E-satis

ฉันคิดว่าสิ่งเหล่าget_list_values_as_xxxนี้เป็นเครื่องกำเนิดไฟฟ้าธรรมดาที่มีบรรทัดเดียวfor x in input_param: yield int(x)และอีกสองรายการตามลำดับด้วยstrและfloat
madtyn

@NiklasB กำลัง "ดึงข้อมูลจากโครงสร้างข้อมูลแบบเรียกซ้ำ" ฉันเพิ่งเข้าสู่ Py เพื่อหาข้อมูล คุณสามารถแทงที่Q นี้ได้ไหม?
alancalvitti

33

เมื่อใดก็ตามที่คุณเรียกกำเนิดจากภายในเครื่องกำเนิดไฟฟ้าที่คุณต้องการ "ปั๊ม" อีกครั้งค่า:yield for v in inner_generator: yield vเมื่อ PEP ชี้ให้เห็นว่ามีความซับซ้อนเล็กน้อยต่อสิ่งนี้ซึ่งคนส่วนใหญ่ไม่สนใจ การควบคุมการไหลของข้อมูลในท้องถิ่นไม่ใช่throw()เป็นตัวอย่างหนึ่งที่ให้ไว้ใน PEP ใช้ไวยากรณ์ใหม่yield from inner_generatorทุกที่ที่คุณจะเขียนforวนซ้ำอย่างชัดเจนมาก่อน มันไม่ได้เป็นแค่น้ำตาลซินแทคติค แต่: มันจัดการกับทุกมุมที่ถูกละเว้นจากforลูป การเป็น "หวาน" ส่งเสริมให้คนใช้และทำให้ได้พฤติกรรมที่เหมาะสม

ข้อความนี้ในหัวข้อการสนทนาพูดถึงความซับซ้อนเหล่านี้:

ด้วยคุณสมบัติตัวสร้างเพิ่มเติมที่แนะนำโดย PEP 342 นั่นไม่ได้เป็นเช่นนั้นอีกต่อไปตามที่อธิบายไว้ใน PEP ของ Greg การวนซ้ำอย่างง่ายไม่สนับสนุน send () และ throw () อย่างถูกต้อง ยิมนาสติกจำเป็นต้องสนับสนุน send () และ throw () จริง ๆ แล้วไม่ซับซ้อนเมื่อคุณทำลายมัน แต่มันก็ไม่สำคัญเหมือนกัน

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

yield fromไวยากรณ์ใหม่ไม่ได้เพิ่มความสามารถเพิ่มเติมใด ๆ ให้กับภาษาในแง่ของเธรดเพียงแค่ทำให้ง่ายต่อการใช้คุณสมบัติที่มีอยู่อย่างถูกต้อง หรือแม่นยำยิ่งขึ้นทำให้ง่ายขึ้นสำหรับผู้ใช้งานมือใหม่ในเครื่องกำเนิดไฟฟ้าภายในที่ซับซ้อนซึ่งเขียนโดยผู้เชี่ยวชาญเพื่อส่งผ่านเครื่องกำเนิดไฟฟ้านั้นโดยไม่ทำลายคุณสมบัติที่ซับซ้อนใด ๆ


23

ตัวอย่างสั้น ๆ จะช่วยให้คุณเข้าใจyield fromกรณีการใช้งานอย่างใดอย่างหนึ่ง: รับค่าจากตัวสร้างอื่น

def flatten(sequence):
    """flatten a multi level list or something
    >>> list(flatten([1, [2], 3]))
    [1, 2, 3]
    >>> list(flatten([1, [2], [3, [4]]]))
    [1, 2, 3, 4]
    """
    for element in sequence:
        if hasattr(element, '__iter__'):
            yield from flatten(element)
        else:
            yield element

print(list(flatten([1, [2], [3, [4]]])))

2
แค่อยากจะแนะนำว่าการพิมพ์ในตอนท้ายจะดูดีกว่านิดหน่อยหากไม่มีการแปลงเป็นรายการ -print(*flatten([1, [2], [3, [4]]]))
yoniLavi

6

yield from โดยทั่วไปโซ่ตัววนซ้ำในวิธีที่มีประสิทธิภาพ:

# chain from itertools:
def chain(*iters):
    for it in iters:
        for item in it:
            yield item

# with the new keyword
def chain(*iters):
    for it in iters:
        yield from it

อย่างที่คุณเห็นมันลบ Python loop หนึ่งอัน นั่นเป็นสิ่งที่มันทำได้ทั้งหมด แต่การโยงตัววนซ้ำเป็นรูปแบบธรรมดาใน Python

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

เครื่องกำเนิดไฟฟ้าค่อนข้างคล้ายกับเธรดในแง่นี้: อนุญาตให้คุณระบุจุดเฉพาะ (เมื่อใดก็ตามที่พวกเขาyield) ที่คุณสามารถกระโดดเข้าและออกได้ เมื่อใช้วิธีนี้กำเนิดจะเรียกว่า coroutines

อ่านบทเรียนที่ยอดเยี่ยมเกี่ยวกับ coroutines ใน Python สำหรับรายละเอียดเพิ่มเติม


10
คำตอบนี้ทำให้เข้าใจผิดเพราะมันช่วยให้คุณสมบัติเด่นของ "ผลตอบแทนจาก" ดังกล่าวข้างต้น: สนับสนุน send () และ throw ()
จัสติน W

2
@Justin W: ฉันเดาว่าสิ่งที่คุณอ่านมาก่อนนั้นจะทำให้เข้าใจผิดเพราะคุณไม่ได้รับจุดที่throw()/send()/close()เป็นyieldคุณสมบัติที่yield fromเห็นได้ชัดว่ามีการใช้งานอย่างถูกต้องตามที่ควรจะลดความซับซ้อนของรหัส เรื่องไร้สาระเช่นนั้นไม่มีส่วนเกี่ยวข้องกับการใช้งาน
Jochen Ritzel

5
คุณโต้แย้งคำตอบของเบ็นแจ็คสันข้างต้นหรือไม่ การอ่านคำตอบของคุณคือน้ำตาล syntactic ซึ่งตามหลังการแปลงรหัสที่คุณให้ไว้ คำตอบของเบ็คแจ็คสันนั้นเป็นการโต้แย้งโดยเฉพาะที่อ้างว่า
Justin W

@JochenRitzel คุณไม่จำเป็นต้องเขียนchainฟังก์ชั่นของคุณเองเพราะitertools.chainมีอยู่แล้ว yield from itertools.chain(*iters)ใช้
คิวเมนตัส

3

ในการใช้งานที่ใช้สำหรับcoroutine Asynchronous IO , yield fromมีพฤติกรรมคล้ายกันawaitในฟังก์ชั่น coroutine ทั้งสองอย่างนี้ใช้เพื่อหยุดการทำงานของ coroutine

สำหรับ Asyncio หากไม่จำเป็นต้องสนับสนุน Python เวอร์ชันเก่า (เช่น> 3.5) async def/ awaitเป็นไวยากรณ์ที่แนะนำเพื่อกำหนด Coroutine ดังนั้นจึงyield fromไม่จำเป็นใน coroutine

แต่โดยทั่วไปนอก asyncio yield from <sub-generator>ยังคงมีการใช้งานอื่นในการวนซ้ำตัวสร้างย่อยตามที่ระบุไว้ในคำตอบก่อนหน้านี้


1

รหัสนี้กำหนดฟังก์ชั่นfixed_sum_digitsกลับมากำเนิดที่ระบุตัวเลขทั้งหกหลักเช่นผลรวมของหลักคือ 20

def iter_fun(sum, deepness, myString, Total):
    if deepness == 0:
        if sum == Total:
            yield myString
    else:  
        for i in range(min(10, Total - sum + 1)):
            yield from iter_fun(sum + i,deepness - 1,myString + str(i),Total)

def fixed_sum_digits(digits, Tot):
    return iter_fun(0,digits,"",Tot) 

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

ฉันคิดว่าสำหรับกรณีเช่นนี้: การเยี่ยมชมต้นไม้yield fromทำให้รหัสง่ายขึ้นและสะอาดขึ้น


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