คำหลักผลตอบแทนทำอะไร?

การใช้คำหลัก yield ใน Python คืออะไร? มันทำอะไร

ตัวอย่างเช่นฉันพยายามที่จะเข้าใจรหัสนี้ 1 :

 def _get_child_candidates(self, distance, min_dist, max_dist): if self._leftchild and distance - max_dist < self._median: yield self._leftchild if self._rightchild and distance + max_dist >= self._median: yield self._rightchild 

และนี่คือตัวหมุนหมายเลข

 result, candidates = [], [self] while candidates: node = candidates.pop() distance = node._get_dist(obj) if distance <= max_dist and distance >= min_dist: result.extend(node._values) candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) return result 

จะเกิดอะไรขึ้นเมื่อ _get_child_candidates มีการส่งคืนรายการหรือไม่ รายการเดียว? มันจะเรียกอีกครั้ง? การโทรติดตามจะหยุดเมื่อใด


1. รหัสนำมาจาก Jochen Schulz (jrschulz) ซึ่งเป็นผู้สร้างห้องสมุด Python ที่ยอดเยี่ยมสำหรับการเว้นวรรค นี่คือลิงก์ไปยังแหล่งข้อมูลเต็มรูปแบบ: โมดูล mspace

8911
อเล็กซ์ให้ 24 окт. ส. 24 ต.ค. 2008-10-24 01:21 '08 เวลา 1:21 น. 2008-10-24 01:21
@ 46 คำตอบ
  • 1
  • 2

เพื่อให้เข้าใจว่า yield อะไรคุณต้องเข้าใจว่าเครื่องกำเนิดไฟฟ้าคืออะไร และก่อนที่เครื่องปั่นไฟจะมาทำซ้ำ

iterables

เมื่อคุณสร้างรายการคุณสามารถอ่านรายการได้ทีละรายการ การอ่านองค์ประกอบของมันทีละคนเรียกว่าการวนซ้ำ:

 >>> mylist = [1, 2, 3] >>> for i in mylist: ... print(i) 1 2 3 

mylist ซ้ำได้ เมื่อคุณใช้ความเข้าใจในรายการคุณจะสร้างรายการและทำซ้ำได้:

 >>> mylist = [x*x for x in range(3)] >>> for i in mylist: ... print(i) 0 1 4 

สิ่งที่คุณสามารถใช้ " for... in... " นั้นซ้ำได้ lists strings ไฟล์ ...

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

เครื่องกำเนิดไฟฟ้า

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

 >>> mygenerator = (x*x for x in range(3)) >>> for i in mygenerator: ... print(i) 0 1 4 

สิ่งนี้จะเหมือนกันยกเว้นว่าคุณใช้ () แทน [] แต่คุณ ไม่สามารถ ทำได้ for я in mygenerator ครั้งที่สองเนื่องจากเครื่องกำเนิดไฟฟ้าสามารถใช้งานได้เพียงครั้งเดียว: พวกมันคำนวณ 0 จากนั้นลืมมันและคำนวณ 1 และสิ้นสุดการคำนวณ 4 หลังจากนั้นอีกหนึ่ง

ยอมแพ้

yield เป็นคีย์เวิร์ดที่ใช้เป็นการ return ยกเว้นว่าฟังก์ชันจะส่งคืนตัวกำเนิด

 >>> def createGenerator(): ... mylist = range(3) ... for i in mylist: ... yield i*i ... >>> mygenerator = createGenerator() # create a generator >>> print(mygenerator) # mygenerator is an object! <generator object createGenerator at 0xb7555c34> >>> for i in mygenerator: ... print(i) 0 1 4 

นี่คือตัวอย่างที่ไร้ประโยชน์ แต่มันมีประโยชน์เมื่อคุณรู้ว่าฟังก์ชั่นของคุณจะคืนค่าจำนวนมากที่คุณต้องอ่านเพียงครั้งเดียว

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

จากนั้นรหัสของคุณจะดำเนินการต่อจากจุดที่ค้างไว้ในแต่ละครั้ง for ใช้งานตัวสร้าง

ตอนนี้ส่วนที่ยากที่สุด:

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

เครื่องกำเนิดไฟฟ้าถือว่าว่างเปล่าหลังจากฟังก์ชั่นเริ่มต้น แต่ไม่ได้รับ yield อีกต่อไป นี่อาจเป็นเพราะความจริงที่ว่าวงจรได้สิ้นสุดลงหรือเนื่องจากความจริงที่ว่าคุณไม่พอใจ "if/else" อีกต่อไป


รหัสของคุณอธิบาย

เครื่องกำเนิดไฟฟ้า:

 # Here you create the method of the node object that will return the generator def _get_child_candidates(self, distance, min_dist, max_dist): # Here is the code that will be called each time you use the generator object: # If there is still a child of the node object on its left # AND if distance is ok, return the next child if self._leftchild and distance - max_dist < self._median: yield self._leftchild # If there is still a child of the node object on its right # AND if distance is ok, return the next child if self._rightchild and distance + max_dist >= self._median: yield self._rightchild # If the function arrives here, the generator will be considered empty # there is no more than two values: the left and the right children 

Caller:

 # Create an empty list and a list with the current object reference result, candidates = list(), [self] # Loop on candidates (they contain only one element at the beginning) while candidates: # Get the last candidate and remove it from the list node = candidates.pop() # Get the distance between obj and the candidate distance = node._get_dist(obj) # If distance is ok, then you can fill the result if distance <= max_dist and distance >= min_dist: result.extend(node._values) # Add the children of the candidate in the candidates list # so the loop will keep running until it will have looked # at all the children of the children of the children, etc. of the candidate candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) return result 

รหัสนี้มีหลายส่วนที่ฉลาด:

  • วัฏจักรถูกทำซ้ำในรายการ แต่รายการจะขยายระหว่างวนซ้ำ :-) นี่เป็นวิธีสั้น ๆ ในการดูข้อมูลที่ซ้อนกันทั้งหมดนี้แม้ว่าจะเป็นอันตรายเล็กน้อยเนื่องจากคุณสามารถรับการวนซ้ำไม่สิ้นสุด ในกรณีนี้ candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) หมดค่าตัวกำเนิดทั้งหมด แต่ while ยังคงสร้างวัตถุตัวสร้างใหม่ที่จะสร้างค่าอื่น ๆ กว่าก่อนหน้านี้เนื่องจากมันไม่ได้ใช้กับโหนดเดียวกัน .

  • การ extend() วิธีการเป็นวิธีการของวัตถุรายการที่รอการทำซ้ำและเพิ่มค่าลงในรายการ

โดยปกติเราให้รายการเขา:

 >>> a = [1, 2] >>> b = [3, 4] >>> a.extend(b) >>> print(a) [1, 2, 3, 4] 

แต่ในรหัสของคุณจะได้รับตัวสร้างซึ่งเป็นสิ่งที่ดีเพราะ:

  1. คุณไม่จำเป็นต้องอ่านค่าสองครั้ง
  2. คุณอาจมีลูกหลายคนและคุณไม่ต้องการให้พวกเขาทั้งหมดถูกเก็บไว้ในความทรงจำ

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

คุณสามารถหยุดที่นี่หรืออ่านนิดหน่อยเพื่อดูการใช้เครื่องกำเนิดไฟฟ้าขั้นสูง:

การควบคุมความอ่อนเพลียของเครื่องกำเนิดไฟฟ้า

 >>> class Bank(): # Let create a bank, building ATMs ... crisis = False ... def create_atm(self): ... while not self.crisis: ... yield "$100" >>> hsbc = Bank() # When everything ok the ATM gives you as much as you want >>> corner_street_atm = hsbc.create_atm() >>> print(corner_street_atm.next()) $100 >>> print(corner_street_atm.next()) $100 >>> print([corner_street_atm.next() for cash in range(5)]) ['$100', '$100', '$100', '$100', '$100'] >>> hsbc.crisis = True # Crisis is coming, no more money! >>> print(corner_street_atm.next()) <type 'exceptions.StopIteration'> >>> wall_street_atm = hsbc.create_atm() # It even true for new ATMs >>> print(wall_street_atm.next()) <type 'exceptions.StopIteration'> >>> hsbc.crisis = False # The trouble is, even post-crisis the ATM remains empty >>> print(corner_street_atm.next()) <type 'exceptions.StopIteration'> >>> brand_new_atm = hsbc.create_atm() # Build a new one to get back in business >>> for cash in brand_new_atm: ... print cash $100 $100 $100 $100 $100 $100 $100 $100 $100 ... 

หมายเหตุ สำหรับ Python 3 ให้ใช้การ print(corner_street_atm.__next__()) หรือ print(next(corner_street_atm))

สิ่งนี้มีประโยชน์สำหรับสิ่งต่าง ๆ เช่นการควบคุมการเข้าถึงทรัพยากร

Itertools เพื่อนที่ดีที่สุดของคุณ

โมดูล itertools มีฟังก์ชั่นพิเศษสำหรับการจัดการการทำซ้ำ คุณเคยต้องการที่จะทำสำเนากำเนิดหรือไม่? สายโซ่ของเครื่องปั่นไฟสองเครื่อง? ค่ากลุ่มในรายการซ้อนกับหนึ่งบรรทัด? Map/Zip โดยไม่ต้องสร้างรายการอื่นหรือไม่

จากนั้นเพียงแค่ import itertools

ตัวอย่างเช่น ลองมาดูขั้นตอนการมาถึงของการแข่งม้าที่เป็นไปได้:

 >>> horses = [1, 2, 3, 4] >>> races = itertools.permutations(horses) >>> print(races) <itertools.permutations object at 0xb754f1dc> >>> print(list(itertools.permutations(horses))) [(1, 2, 3, 4), (1, 2, 4, 3), (1, 3, 2, 4), (1, 3, 4, 2), (1, 4, 2, 3), (1, 4, 3, 2), (2, 1, 3, 4), (2, 1, 4, 3), (2, 3, 1, 4), (2, 3, 4, 1), (2, 4, 1, 3), (2, 4, 3, 1), (3, 1, 2, 4), (3, 1, 4, 2), (3, 2, 1, 4), (3, 2, 4, 1), (3, 4, 1, 2), (3, 4, 2, 1), (4, 1, 2, 3), (4, 1, 3, 2), (4, 2, 1, 3), (4, 2, 3, 1), (4, 3, 1, 2), (4, 3, 2, 1)] 

ทำความเข้าใจกับกลไกการทำซ้ำภายใน

การทำซ้ำเป็นกระบวนการที่แสดงถึงการวนซ้ำ (การใช้ __iter__() ) และการวนซ้ำ (การนำ __iter__() ) การทำซ้ำเป็นวัตถุใด ๆ ที่คุณสามารถรับตัววนซ้ำ การวนซ้ำเป็นวัตถุที่อนุญาตให้คุณทำซ้ำซ้ำได้

มีเพิ่มเติมเกี่ยวกับเรื่องนี้ในบทความนี้เกี่ยวกับ วิธีการ วนซ้ำการทำงาน

13022
24 окт. คำตอบจะได้รับจาก e-Satisfaction 24 ตุลาคม 2008-10-24 01:48 '08 เวลา 1:48 2008-10-24 01:48

ป้ายชื่อเพื่อ yield Grocking

เมื่อคุณเห็นฟังก์ชันที่มี yield ใช้เคล็ดลับง่าย ๆ นี้เพื่อทำความเข้าใจว่าจะเกิดอะไรขึ้น:

  1. แทรก result = [] ที่ตำแหน่งเริ่มต้นของฟังก์ชัน
  2. แทนที่แต่ละ yield expr ด้วย result.append(expr)
  3. แทรกผลลัพธ์ของบรรทัด return result ที่ด้านล่างของฟังก์ชัน
  4. Yay - ไม่ได้ประกาศ yield อีกต่อไป! อ่านและค้นหารหัส
  5. เปรียบเทียบฟังก์ชันกับข้อกำหนดดั้งเดิม

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

อย่าสับสนตัววนซ้ำตัววนซ้ำและตัวกำเนิดไฟฟ้าของคุณ

ก่อนอื่น โปรโตคอล iterator - เมื่อคุณเขียน

 for x in mylist: ...loop body... 

Python ดำเนินการสองขั้นตอนต่อไปนี้:

  1. รับตัววนซ้ำสำหรับ mylist :

    การเรียก iter(mylist) →คืนค่าออบเจคด้วยเมธอด ( next() (หรือ __next__() next() __next__() ใน Python 3)

    [นี่คือขั้นตอนที่คนส่วนใหญ่ลืมพูดถึง]

  2. ใช้ตัววนซ้ำเพื่อวนองค์ประกอบ:

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

ความจริงก็คือ Python ดำเนินการสองขั้นตอนข้างต้นได้ตลอดเวลาเมื่อต้องการวนซ้ำเนื้อหาของวัตถุ - เพื่อให้สามารถวนซ้ำได้ แต่มันอาจเป็นโค้ดเช่น otherlist.extend(mylist) (โดยที่รายการอื่นเป็นรายการ Python )

border=0

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

ดังนั้นในโปรโตคอลตัววนซ้ำวัตถุหลายตัวใช้โปรโตคอลนี้:

  1. รายการพจนานุกรม tuples ชุดไฟล์ในตัว
  2. คลาสแบบกำหนดเองที่ใช้ __iter__()
  3. เครื่องปั่นไฟ

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

 def f123(): yield 1 yield 2 yield 3 for item in f123(): print item 

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

ดังนั้นวัตถุเครื่องกำเนิดไฟฟ้าจะคล้ายกับอะแดปเตอร์ - ที่ปลายด้านหนึ่งมันแสดงให้เห็นถึงโปรโตคอล iterator ให้ __iter__() และ next() เพื่อรักษาห่วง for ในสภาพที่ดี อย่างไรก็ตามในอีกด้านหนึ่งมันเริ่มต้นฟังก์ชั่นที่เพียงพอที่จะรับค่าถัดไปและทำให้มันกลับสู่โหมดสแตนด์บาย

ทำไมต้องใช้เครื่องกำเนิดไฟฟ้า

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

1744
26 окт. ตอบโดย user28409 26 ต.ค. 2008-10-26 00:22 '08 เวลา 0:22 2008-10-26 00:22

ลองคิดแบบนี้สิ

ตัววนซ้ำเป็นคำแฟนซีสำหรับวัตถุที่มีเมธอดถัดไป () ดังนั้นในที่สุดฟังก์ชั่นผลผลิต -ed ดูเหมือนว่า:

รุ่นดั้งเดิม:

 def some_function(): for i in xrange(4): yield i for i in some_function(): print i 

นี่คือสิ่งที่ Python interpreter ทำกับโค้ดด้านบน:

 class it: def __init__(self): # Start at -1 so that we get 0 when we add 1 below. self.count = -1 # The __iter__ method will be called once by the 'for' loop. # The rest of the magic happens on the object returned by this method. # In this case it is the object itself. def __iter__(self): return self # The next method will be called repeatedly by the 'for' loop # until it raises StopIteration. def next(self): self.count += 1 if self.count < 4: return self.count else: # A StopIteration exception is raised # to signal that the iterator is done. # This is caught implicitly by the 'for' loop. raise StopIteration def some_func(): return it() for i in some_func(): print i 

เพื่อให้เข้าใจสิ่งที่เกิดขึ้นเบื้องหลังได้ดียิ่งขึ้นการวนซ้ำสามารถเขียนใหม่ได้ดังนี้:

 iterator = some_func() try: while 1: print iterator.next() except StopIteration: pass 

มันสมเหตุสมผลมากกว่าหรือแค่ทำให้คุณงง? :)

ฉันต้องชี้ให้เห็นว่านี่เป็นการทำให้เข้าใจง่ายสำหรับวัตถุประสงค์ในการอธิบาย :)

441
24 окт. ตอบกลับ Jason Baker เมื่อวันที่ 24 ต.ค. 2008-10-24 01:28 '08 เวลา 1:28 2008-10-24 01:28

คำหลัก yield ลดลงมาถึงข้อเท็จจริงง่ายๆสองประการ:

  1. หากคอมไพเลอร์ตรวจพบคำหลัก yield ที่ใดก็ได้ภายในฟังก์ชั่นฟังก์ชั่นนี้จะไม่ถูกส่งกลับผ่าน return แต่ จะส่งคืน วัตถุลิสต์รอดำเนินการที่ เรียกว่าตัวสร้างทันที
  2. เครื่องกำเนิดซ้ำ ทำซ้ำได้คืออะไร? นี่คือบางสิ่งบางอย่างเช่น range set list หรือ dict-view พร้อมโพรโทคอลแบบฝังสำหรับการเยี่ยมชมแต่ละรายการตามลำดับที่ระบุ

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

 generator = myYieldingFunction(...) x = list(generator) generator v [x[0], ..., ???] generator v [x[0], x[1], ..., ???] generator v [x[0], x[1], x[2], ..., ???] StopIteration exception [x[0], x[1], x[2]] done list==[x[0], x[1], x[2]] 

ตัวอย่าง

มากำหนดฟังก์ชั่น makeRange ซึ่งคล้ายกับ range Python การ makeRange(n) GENERATOR:

 def makeRange(n): # return 0,1,2,...,n-1 i = 0 while i < n: yield i i += 1 >>> makeRange(5) <generator object makeRange at 0x19e4aa0> 

ในการทำให้เครื่องกำเนิดกลับค่าที่ค้างอยู่ทันทีคุณสามารถส่งต่อไปยัง list() (เช่นเดียวกับตัววนซ้ำใด ๆ ):

 >>> list(makeRange(5)) [0, 1, 2, 3, 4] 

เปรียบเทียบตัวอย่างกับ "เพิ่งส่งคืนรายการ"

ตัวอย่างข้างต้นสามารถดูได้เพียงแค่สร้างรายการที่คุณเพิ่มและส่งคืน:

 # list-version # # generator-version def makeRange(n): # def makeRange(n): """return [0,1,2,...,n-1]""" #~ """return 0,1,2,...,n-1""" TO_RETURN = [] #> i = 0 # i = 0 while i < n: # while i < n: TO_RETURN += [i] #~ yield i i += 1 # i += 1 ## indented return TO_RETURN #> >>> makeRange(5) [0, 1, 2, 3, 4] 

อย่างไรก็ตามมีความแตกต่างที่สำคัญอย่างหนึ่งคือ ดูส่วนสุดท้าย


คุณจะใช้เครื่องกำเนิดไฟฟ้าอย่างไร

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

 # _ITERABLE_ >>> [x+10 for x in makeRange(5)] [10, 11, 12, 13, 14] 

เพื่อทำความเข้าใจกับเครื่องกำเนิดไฟฟ้าให้ดีขึ้นคุณสามารถเล่นกับโมดูล itertools (ต้องแน่ใจว่าใช้ chain.from_iterable และไม่ใช่ chain มีการรับประกัน chain จาก chain.from_iterable ) ตัวอย่างเช่นคุณสามารถใช้เครื่องกำเนิดไฟฟ้าเพื่อใช้รายการสันหลังยาวที่ยาวไม่สิ้นสุดเช่น itertools.count() คุณสามารถใช้การ def enumerate(iterable): zip(count(), iterable) ของคุณเอง def enumerate(iterable): zip(count(), iterable) หรือทำได้โดยใช้คีย์เวิร์ด yield ในการวนรอบสักครู่

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


เบื้องหลัง

นี่คือการทำงานของ Python Iteration Protocol นั่นคือสิ่งที่เกิดขึ้นเมื่อคุณทำ list(makeRange(5)) นี่คือสิ่งที่ฉันอธิบายก่อนหน้าว่า "ขี้เกียจรายการพิเศษ"

 >>> x=iter(range(5)) >>> next(x) 0 >>> next(x) 1 >>> next(x) 2 >>> next(x) 3 >>> next(x) 4 >>> next(x) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration 

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


สิ่งเล็กน้อย

โดยปกติแล้วคนส่วนใหญ่ไม่สนใจความแตกต่างดังต่อไปนี้และอาจต้องการหยุดอ่านที่นี่

ใน Python การวนซ้ำเป็นวัตถุใด ๆ ที่ "เข้าใจแนวคิดของการวนซ้ำ" ตัวอย่างเช่นรายการ [1,2,3] และตัววนซ้ำเป็นอินสแตนซ์เฉพาะของการร้องขอลูปตัวอย่างเช่น [1,2,3].__iter__() . ตัวกำเนิดนั้นเหมือนกับตัววนซ้ำใด ๆ ยกเว้นวิธีการเขียน (ด้วยไวยากรณ์ของฟังก์ชัน)

เมื่อคุณร้องขอตัววนซ้ำจากรายการมันจะสร้างตัววนซ้ำใหม่ อย่างไรก็ตามเมื่อคุณร้องขอตัววนซ้ำจากตัววนซ้ำ (ซึ่งคุณไม่ค่อยได้ทำ) ก็จะให้สำเนาของมัน

ดังนั้นในกรณีที่ไม่น่าเป็นไปได้ที่คุณจะไม่สามารถทำอะไรเช่นนั้น ...

 > x = myRange(5) > list(x) [0, 1, 2, 3, 4] > list(x) [] 

... จากนั้นจำไว้ว่าตัวกำเนิดเป็นตัววนซ้ำ เช่นใช้ครั้งเดียว หากคุณต้องการนำมาใช้ซ้ำคุณควร myRange(...) โทรหา myRange(...) หากคุณต้องการใช้ผลลัพธ์สองครั้งให้แปลงผลลัพธ์เป็นรายการและบันทึกไว้ในรายการตัวแปร x = list(myRange(5)) ผู้ที่ต้องการโคลนเครื่องกำเนิดไฟฟ้า (ตัวอย่างเช่นผู้ดำเนินการแฮ็กเกอร์ metaprogramming) สามารถใช้ itertools.tee หากจำเป็นอย่างยิ่งเนื่องจากมาตรฐาน Python PEP สำหรับผู้ทำ ซ้ำนั้นล่าช้า

378
19 июня '11 в 9:33 2011-06-19 09:33 คำตอบจะได้รับ ninjagecko 19 มิถุนายน 11 ที่ 9:33 2011-06-19 09:33

คำหลัก yield อะไรทำในหลาม?

โครงการตอบสนอง / สรุป

  • ฟังก์ชั่น yield จากการโทร จะส่งคืน เครื่องกำเนิดไฟฟ้า
  • เครื่องกำเนิดไฟฟ้าเป็นตัววนซ้ำเนื่องจากพวกมันใช้ โปรโตคอลตัววนซ้ำ ดังนั้นคุณจึงสามารถทำซ้ำได้
  • Генератору также может быть отправлена информация , что делает его концептуально сопрограммой .
  • В Python 3 вы можете делегировать от одного генератора другому в обоих направлениях с помощью yield from .
  • (Приложение критикует пару @, включая верхний, и обсуждает использование return в генераторе.)

Генераторы:

yield допустим только внутри определения функции, и включение yield в определение функции заставляет его возвращать генератор.

Идея для генераторов исходит из других языков (см. Сноску 1) с различными реализациями. В Python Generators выполнение кода заморожено в точке выхода. Когда вызывается генератор (методы обсуждаются ниже), выполнение возобновляется, а затем останавливается при следующем выходе.

yield предоставляет простой способ реализации протокола итератора , который определяется следующими двумя методами: __iter__ и next (Python 2) или __next__ (Python 3). Оба эти метода делают объект итератором, который можно проверить типом с помощью абстрактного базового класса Iterator из модуля collections .

 >>> def func(): ... yield 'I am' ... yield 'a generator!' ... >>> type(func) # A function with yield is still a function <type 'function'> >>> gen = func() >>> type(gen) # but it returns a generator <type 'generator'> >>> hasattr(gen, '__iter__') # that an iterable True >>> hasattr(gen, 'next') # and with .next (.__next__ in Python 3) True # implements the iterator protocol.