คุณสามารถใช้ฟังก์ชันตัวสร้าง Python เพื่ออะไรได้บ้าง


213

ฉันเริ่มที่จะเรียนรู้ Python และฉันได้พบกับฟังก์ชั่นเครื่องกำเนิด ฉันต้องการทราบว่าปัญหาประเภทใดที่หน้าที่เหล่านี้ดีในการแก้ไข


6
อาจเป็นคำถามที่ดีกว่าเมื่อเราไม่ควรใช้ 'em
cregox

1
ตัวอย่างโลกแห่งความจริงที่นี่
Giri

คำตอบ:


239

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

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

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

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

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

big_list = list(the_generator)

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

@StevenLu: หากไม่มีปัญหาในการเปิดใช้เธรดด้วยตนเองก่อนyieldและjoinหลังจากที่ได้รับผลลัพธ์ถัดไปจะไม่ทำงานแบบขนาน (และไม่มีตัวสร้างไลบรารี่มาตรฐานทำเช่นนี้; เครื่องกำเนิดหยุดที่แต่ละyieldจนกว่าจะมีการร้องขอค่าต่อไป หากเครื่องกำเนิดไฟฟ้ากำลังหุ้ม I / O ระบบปฏิบัติการอาจทำการแคชข้อมูลจากไฟล์โดยสมมติว่ามันจะถูกร้องขอในไม่ช้า แต่นั่นก็คือระบบปฏิบัติการ Python ไม่เกี่ยวข้อง
ShadowRanger

90

หนึ่งในเหตุผลในการใช้ตัวสร้างคือการทำให้โซลูชันชัดเจนสำหรับโซลูชันบางชนิด

อีกวิธีหนึ่งคือการจัดการผลลัพธ์ทีละครั้งโดยหลีกเลี่ยงการสร้างรายการผลลัพธ์ขนาดใหญ่ที่คุณจะดำเนินการแยกออกจากกัน

หากคุณมีฟังก์ชั่น fibonacci-up-to-n ดังนี้:

# function version
def fibon(n):
    a = b = 1
    result = []
    for i in xrange(n):
        result.append(a)
        a, b = b, a + b
    return result

คุณสามารถเขียนฟังก์ชันได้ง่ายขึ้นเช่นนี้:

# generator version
def fibon(n):
    a = b = 1
    for i in xrange(n):
        yield a
        a, b = b, a + b

ฟังก์ชั่นชัดเจนขึ้น และถ้าคุณใช้ฟังก์ชั่นเช่นนี้:

for x in fibon(1000000):
    print x,

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


18
และถ้าคุณต้องการรายชื่อคุณสามารถทำได้list(fibon(5))
endolith

41

ดูส่วน "แรงจูงใจ" ในPEP 255

การใช้เครื่องกำเนิดไฟฟ้าที่ไม่ชัดเจนกำลังสร้างฟังก์ชั่นอินเตอร์รัปต์ซึ่งช่วยให้คุณทำสิ่งต่าง ๆ เช่นอัปเดต UI หรือเรียกใช้งานหลายอย่าง "พร้อมกัน" (interleaved จริง ๆ ) ในขณะที่ไม่ได้ใช้เธรด


1
ส่วนแรงจูงใจเป็นสิ่งที่ดีที่มีตัวอย่างเฉพาะ: "เมื่อฟังก์ชั่นผู้ผลิตมีงานหนักพอที่จะต้องรักษาสถานะระหว่างค่าที่ผลิตภาษาการเขียนโปรแกรมส่วนใหญ่ไม่มีวิธีที่น่าพอใจและมีประสิทธิภาพเกินกว่าการเพิ่มฟังก์ชั่นการโทรกลับ รายการ ... ตัวอย่างเช่น tokenize.py ในไลบรารีมาตรฐานใช้แนวทางนี้ "
Ben Creasy

38

ฉันพบคำอธิบายที่ล้างข้อสงสัยของฉันนี้ เพราะมีความเป็นไปได้ที่คนที่ไม่รู้จักGeneratorsก็ไม่รู้เหมือนกันyield

กลับ

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

ผล

แต่ถ้าตัวแปรท้องถิ่นไม่ถูกโยนทิ้งเมื่อเราออกจากฟังก์ชั่น นี่ก็หมายความว่าเราสามารถทำสิ่งresume the functionที่เราทิ้งไว้ได้ นี่คือที่แนวคิดของการgeneratorsแนะนำและyieldคำสั่งดำเนินการต่อที่functionซ้ายออก

  def generate_integers(N):
    for i in xrange(N):
    yield i

    In [1]: gen = generate_integers(3)
    In [2]: gen
    <generator object at 0x8117f90>
    In [3]: gen.next()
    0
    In [4]: gen.next()
    1
    In [5]: gen.next()

นั่นคือข้อแตกต่างระหว่างreturnและyieldข้อความใน Python

คำชี้แจงผลตอบแทนเป็นสิ่งที่ทำให้ฟังก์ชั่นเป็นเครื่องกำเนิดไฟฟ้า

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


33

ตัวอย่างโลกแห่งความจริง

สมมติว่าคุณมี 100 ล้านโดเมนในตาราง MySQL ของคุณและคุณต้องการอัปเดตอันดับ Alexa สำหรับแต่ละโดเมน

สิ่งแรกที่คุณต้องมีคือเลือกชื่อโดเมนจากฐานข้อมูล

สมมติว่าชื่อตารางของคุณและชื่อคอลัมน์domainsdomain

ถ้าคุณใช้SELECT domain FROM domainsมันจะส่งคืน 100 ล้านแถวซึ่งจะใช้หน่วยความจำมาก ดังนั้นเซิร์ฟเวอร์ของคุณอาจมีปัญหา

ดังนั้นคุณตัดสินใจที่จะรันโปรแกรมแบบแบตช์ สมมุติว่าขนาดแบทช์ของเราคือ 1,000

ในชุดแรกของเราเราจะค้นหา 1,000 แถวแรกตรวจสอบอันดับ Alexa สำหรับแต่ละโดเมนและอัปเดตแถวฐานข้อมูล

ในชุดที่สองของเราเราจะทำงานใน 1,000 แถวถัดไป ในชุดที่สามของเรามันจะมาจาก 2001 ถึง 3000 และอื่น ๆ

ตอนนี้เราต้องการฟังก์ชั่นเครื่องกำเนิดไฟฟ้าที่สร้างแบทช์ของเรา

นี่คือฟังก์ชั่นเครื่องกำเนิดของเรา:

def ResultGenerator(cursor, batchsize=1000):
    while True:
        results = cursor.fetchmany(batchsize)
        if not results:
            break
        for result in results:
            yield result

อย่างที่คุณเห็นฟังก์ชั่นของเรายังคงให้yieldผลลัพธ์ หากคุณใช้คำหลักreturnแทนyieldฟังก์ชั่นทั้งหมดจะสิ้นสุดลงเมื่อถึงการส่งคืน

return - returns only once
yield - returns multiple times

หากฟังก์ชั่นใช้คำหลักyieldแล้วมันเป็นเครื่องกำเนิดไฟฟ้า

ตอนนี้คุณสามารถทำซ้ำแบบนี้:

db = MySQLdb.connect(host="localhost", user="root", passwd="root", db="domains")
cursor = db.cursor()
cursor.execute("SELECT domain FROM domains")
for result in ResultGenerator(cursor):
    doSomethingWith(result)
db.close()

มันจะเป็นจริงมากขึ้นถ้าผลผลิตสามารถอธิบายได้ในแง่ของการเขียนโปรแกรมแบบเรียกซ้ำ / dyanmic!
igaurav

27

บัฟเฟอร์ เมื่อมีประสิทธิภาพในการดึงข้อมูลในหน่วยขนาดใหญ่ แต่ประมวลผลเป็นชิ้นเล็ก ๆ จากนั้นตัวสร้างอาจช่วย:

def bufferedFetch():
  while True:
     buffer = getBigChunkOfData()
     # insert some code to break on 'end of data'
     for i in buffer:    
          yield i

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


3
ถ้า getBigChuckOfData ไม่ขี้เกียจฉันก็ไม่เข้าใจว่าผลประโยชน์คืออะไรที่นี่ กรณีการใช้งานสำหรับฟังก์ชั่นนี้คืออะไร?
Sean Geoffrey Pietz

1
แต่ประเด็นคือ IIUC, bufferedFetch กำลังขี้เกียจเรียกไปที่ getBigChunkOfData ถ้า getBigChunkOfData ขี้เกียจแล้วบัฟเฟอร์ที่เรียกมาจะไม่มีประโยชน์ แต่ละการเรียกไปยัง bufferedFetch () จะคืนค่าองค์ประกอบบัฟเฟอร์หนึ่งแม้ว่า BigChunk จะถูกอ่านไปแล้วและคุณไม่จำเป็นต้องนับองค์ประกอบต่อไปเพื่อกลับมาอย่างชัดเจน
hmijail mourns ลาออก

21

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

ตัวอย่างนามธรรมจะเป็นตัวสร้างตัวเลขฟีโบนักชีที่ไม่ได้อยู่ภายในลูปและเมื่อมันถูกเรียกจากที่ใดก็ตามจะส่งคืนหมายเลขถัดไปตามลำดับเสมอ:

def fib():
    first = 0
    second = 1
    yield first
    yield second

    while 1:
        next = first + second
        yield next
        first = second
        second = next

fibgen1 = fib()
fibgen2 = fib()

ตอนนี้คุณมีออบเจ็กต์ตัวสร้างหมายเลข Fibonacci สองตัวซึ่งคุณสามารถโทรจากที่ใดก็ได้ในรหัสของคุณ

>>> fibgen1.next(); fibgen1.next(); fibgen1.next(); fibgen1.next()
0
1
1
2
>>> fibgen2.next(); fibgen2.next()
0
1
>>> fibgen1.next(); fibgen1.next()
3
5

สิ่งที่น่ารักเกี่ยวกับเครื่องกำเนิดไฟฟ้าคือพวกเขาห่อหุ้มรัฐโดยไม่ต้องผ่านห่วงของการสร้างวัตถุ วิธีคิดอย่างหนึ่งเกี่ยวกับพวกเขาก็คือ "หน้าที่" ซึ่งจำสถานะภายในของพวกเขาได้

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


19

คำอธิบายง่ายๆ: พิจารณาforข้อความ

for item in iterable:
   do_stuff()

หลายครั้งที่รายการทั้งหมดในiterableไม่จำเป็นต้องมีตั้งแต่เริ่มต้น แต่สามารถสร้างได้ทันทีตามที่ต้องการ ทั้งสองนี้มีประสิทธิภาพมากขึ้น

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

ในบางครั้งคุณอาจไม่รู้รายการทั้งหมดล่วงหน้า ตัวอย่างเช่น:

for command in user_input():
   do_stuff_with(command)

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

def user_input():
    while True:
        wait_for_command()
        cmd = get_command()
        yield cmd

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


... และลำดับที่ไม่มีที่สิ้นสุดอาจเกิดจากการวนซ้ำในรายการเล็ก ๆ ซ้ำ ๆ กลับไปที่จุดเริ่มต้นหลังจากถึงจุดสิ้นสุด ฉันใช้สิ่งนี้สำหรับการเลือกสีในกราฟหรือการผลิต throbbers หรือสปินเนอร์ที่ยุ่งในข้อความ
Andrej Panjkov

@mataap: มีเป็นitertoolที่ - cyclesดู
martineau

12

สิ่งที่ฉันโปรดปรานคือการใช้งาน "ตัวกรอง" และ "ลด"

สมมติว่าเรากำลังอ่านไฟล์และต้องการเพียงบรรทัดที่ขึ้นต้นด้วย "##"

def filter2sharps( aSequence ):
    for l in aSequence:
        if l.startswith("##"):
            yield l

จากนั้นเราสามารถใช้ฟังก์ชั่นเครื่องกำเนิดไฟฟ้าในวงที่เหมาะสม

source= file( ... )
for line in filter2sharps( source.readlines() ):
    print line
source.close()

ตัวอย่างการลดจะคล้ายกัน สมมติว่าเรามีไฟล์ที่เราต้องการค้นหาบล็อกของ<Location>...</Location>บรรทัด [ไม่ใช่แท็ก HTML แต่บรรทัดที่มีลักษณะคล้ายแท็ก]

def reduceLocation( aSequence ):
    keep= False
    block= None
    for line in aSequence:
        if line.startswith("</Location"):
            block.append( line )
            yield block
            block= None
            keep= False
        elif line.startsWith("<Location"):
            block= [ line ]
            keep= True
        elif keep:
            block.append( line )
        else:
            pass
    if block is not None:
        yield block # A partial block, icky

อีกครั้งเราสามารถใช้ตัวสร้างนี้ในลูปที่เหมาะสม

source = file( ... )
for b in reduceLocation( source.readlines() ):
    print b
source.close()

แนวคิดก็คือฟังก์ชั่นเครื่องกำเนิดไฟฟ้าช่วยให้เราสามารถกรองหรือลดลำดับทำให้เกิดค่าหนึ่งค่าต่อครั้ง


8
fileobj.readlines()จะอ่านไฟล์ทั้งหมดในรายการในหน่วยความจำเอาชนะวัตถุประสงค์ของการใช้เครื่องกำเนิดไฟฟ้า เนื่องจากวัตถุไฟล์นั้นสามารถทำซ้ำได้คุณจึงสามารถใช้for b in your_generator(fileobject):แทน วิธีนี้ไฟล์ของคุณจะถูกอ่านทีละหนึ่งบรรทัดเพื่อหลีกเลี่ยงการอ่านไฟล์ทั้งหมด
nosklo

ลดสถานที่ตั้งเป็นรายการที่ค่อนข้างแปลกทำไมไม่เพียงให้ผลผลิตแต่ละบรรทัด? นอกจากนี้ตัวกรองและลดยังเป็นบิวอินที่มีพฤติกรรมตามที่คาดไว้ (ดูวิธีใช้ใน ipython ฯลฯ ) การใช้ "ลด" ของคุณเหมือนกับตัวกรอง
James Antill

จุดที่ดีใน readlines () ฉันมักจะรู้ว่าไฟล์เป็นตัววนซ้ำบรรทัดแรกในระหว่างการทดสอบหน่วย
S.Lott

ที่จริงแล้ว "การลด" กำลังรวมหลายบรรทัดลงในวัตถุประกอบ ตกลงมันเป็นรายการ แต่ก็ยังลดลงจากแหล่งที่มา
S.Lott

9

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

class Rect():

    def __init__(self, x, y, width, height):
        self.l_top  = (x, y)
        self.r_top  = (x+width, y)
        self.r_bot  = (x+width, y+height)
        self.l_bot  = (x, y+height)

    def __iter__(self):
        yield self.l_top
        yield self.r_top
        yield self.r_bot
        yield self.l_bot

ตอนนี้ฉันสามารถสร้างรูปสี่เหลี่ยมผืนผ้าและวนรอบมุมของมันได้:

myrect=Rect(50, 50, 100, 100)
for corner in myrect:
    print(corner)

แทนที่จะ__iter__คุณอาจจะมีวิธีการที่เรียกว่าiter_corners for corner in myrect.iter_corners()มันเป็นเพียงที่สง่างามมากขึ้นเพื่อใช้__iter__ตั้งแต่นั้นเราสามารถใช้ชื่ออินสแตนซ์ชั้นโดยตรงในforการแสดงออก


ฉันชื่นชอบแนวคิดในการส่งผ่านคลาสคลาสที่คล้ายคลึงกันในฐานะผู้สร้าง
eusoubrasileiro

7

โดยทั่วไปหลีกเลี่ยงฟังก์ชั่นการโทรกลับเมื่อวนซ้ำสถานะการบำรุงรักษาอินพุต

ดูที่นี่และที่นี่เพื่อดูภาพรวมของสิ่งที่สามารถทำได้โดยใช้เครื่องกำเนิดไฟฟ้า


4

บางคำตอบที่ดีที่นี่ แต่ฉันขอแนะนำให้อ่านแบบฝึกหัด Python Functional Programmingอย่างสมบูรณ์ซึ่งจะช่วยอธิบายกรณีการใช้เครื่องกำเนิดไฟฟ้าที่มีประสิทธิภาพมากขึ้น


3

เนื่องจากวิธีการส่งของตัวสร้างไม่ได้ถูกกล่าวถึงนี่คือตัวอย่าง:

def test():
    for i in xrange(5):
        val = yield
        print(val)

t = test()

# Proceed to 'yield' statement
next(t)

# Send value to yield
t.send(1)
t.send('2')
t.send([3])

มันแสดงให้เห็นความเป็นไปได้ในการส่งค่าไปยังเครื่องกำเนิดไฟฟ้าที่ทำงานอยู่ หลักสูตรขั้นสูงเพิ่มเติมเกี่ยวกับเครื่องกำเนิดไฟฟ้าในวิดีโอด้านล่าง (รวมถึงyieldสำรวจเครื่องปั่นไฟสำหรับการประมวลผลแบบขนานการหลีกเลี่ยงการ จำกัด การเรียกซ้ำ ฯลฯ )

David Beazley กับกำเนิดที่ PyCon 2014


2

ฉันใช้เครื่องกำเนิดไฟฟ้าเมื่อเว็บเซิร์ฟเวอร์ของเราทำหน้าที่เป็นพร็อกซี:

  1. ไคลเอนต์ร้องขอ URL พร็อกซีจากเซิร์ฟเวอร์
  2. เซิร์ฟเวอร์เริ่มโหลด URL เป้าหมาย
  3. เซิร์ฟเวอร์ให้ผลตอบแทนแก่ลูกค้าทันทีที่ได้รับ

1

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

def primes():
    primes_found = set()
    primes_found.add(2)
    yield 2
    for i in itertools.count(1):
        candidate = i * 2 + 1
        if not all(candidate % prime for prime in primes_found):
            primes_found.add(candidate)
            yield candidate

จากนั้นคุณสามารถใช้สิ่งนั้นเพื่อสร้างผลิตภัณฑ์ในช่วงเวลาต่อมา:

def prime_products():
    primeiter = primes()
    prev = primeiter.next()
    for prime in primeiter:
        yield prime * prev
        prev = prime

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


ถ้าไม่มี (ผู้สมัคร% prime สำหรับ Prime ใน primes_found) ควรเป็นถ้าทั้งหมด (candidate% prime สำหรับ prime ใน primes_found)
rjmunro

ใช่ฉันหมายถึงการเขียน "ถ้าไม่มี (บอร์ด% prime == 0 สำหรับไพร์มใน primes_found) คุณเป็นคนที่ค่อนข้างเล็ก ๆ น้อย ๆ :)
นิคจอห์นสัน

ฉันเดาว่าคุณลืมลบ 'ไม่' ออกจากถ้าไม่ใช่ทั้งหมด (ตัวเลือก% prime สำหรับ prime ใน primes_found)
Thava

0

ยังดีสำหรับการพิมพ์หมายเลขเฉพาะสูงสุด n:

def genprime(n=10):
    for num in range(3, n+1):
        for factor in range(2, num):
            if num%factor == 0:
                break
        else:
            yield(num)

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