# python yield教學 - “yield”關鍵字有什麼作用？

## yield用法 yield用途 (25)

Python中`yield`關鍵字的用途是什麼？ 它有什麼作用？

``````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
``````

1.代碼來自Jochen Schulz（jrschulz），他為度量空間創建了一個很棒的Python庫。 這是完整源代碼的鏈接： 模塊mspace

``````def fib():
last, cur = 0, 1
while True:
yield cur
last, cur = cur, last + cur
``````

``````for f in fib():
if some_condition: break
coolfuncs(f);
``````

``````def simple_generator():
yield 'one'
yield 'two'
yield 'three'

for i in simple_generator():
print i
``````

``````one
two
three
``````

``````def myRangeNaive(i):
n = 0
range = []
while n < i:
range.append(n)
n = n + 1
return range
``````

``````for i in myRangeNaive(10):
print i
``````

• 您創建一個只使用一次的數組（這會浪費內存）
• 這段代碼實際上循環遍歷該數組兩次！ :(

``````def myRangeSmart(i):
n = 0
while n < i:
yield n
n = n + 1
return

for i in myRangeSmart(10):
print i
``````

1. 嘗試獲取num_list `return`
``````def num_list(n):
for i in range(n):
return i
``````

``````In [5]: num_list(3)
Out[5]: 0
``````

1. 來了 `yield`

``````In [10]: def num_list(n):
...:     for i in range(n):
...:         yield i
...:

In [11]: num_list(3)
Out[11]: <generator object num_list at 0x10327c990>

In [12]: list(num_list(3))
Out[12]: [0, 1, 2]
``````

1. 我們可以`yield`用另一個步驟重寫語句`return`
``````In [15]: def num_list(n):
...:     result = []
...:     for i in range(n):
...:         result.append(i)
...:     return result

In [16]: num_list(3)
Out[16]: [0, 1, 2]
``````

• `return`並且`yield`是雙胞胎
• `list`並且`generator`是雙胞胎

``````from itertools import islice

def fib_gen():
a, b = 1, 1
while True:
yield a
a, b = b, a + b

assert [1, 1, 2, 3, 5] == list(islice(fib_gen(), 5))
``````

``````def ftake(fnext, last):
return [fnext() for _ in xrange(last)]

def fib_gen2():
#funky scope due to python2.x workaround
#for python 3.x use nonlocal
def _():
_.a, _.b = _.b, _.a + _.b
return _.a
_.a, _.b = 0, 1
return _

assert [1,1,2,3,5] == ftake(fib_gen2(), 5)
``````

``````class fib_gen3:
def __init__(self):
self.a, self.b = 1, 1

def __call__(self):
r = self.a
self.a, self.b = self.b, self.a + self.b
return r

assert [1,1,2,3,5] == ftake(fib_gen3(), 5)
``````

``````def isPrimeNumber(n):
if n==1:
return False
for x in range(2,n):
if n % x == 0:
return False
return True

def primes (n=1):
while(True):
print "loop step ---------------- {}".format(n)
n += 1

for n in primes():
if n> 10:break
print "wiriting result {}".format(n)
``````

``````loop step ---------------- 1
loop step ---------------- 2
loop step ---------------- 3
wiriting result 3
loop step ---------------- 4
loop step ---------------- 5
wiriting result 5
loop step ---------------- 6
loop step ---------------- 7
wiriting result 7
loop step ---------------- 8
loop step ---------------- 9
loop step ---------------- 10
loop step ---------------- 11
``````

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

for i in some_function():
print i
``````

``````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
``````

`next()`隨後被調用時，它檢索功能的物品入堆棧，重新蓬勃生機。該功能繼續從它停止的地方進行計算，不知道它剛剛在冷庫中度過了一個永恆的事實。

``````def normalFunction():
return
if False:
pass

def yielderFunction():
return
if False:
yield 12
``````

``````>>> yielderFunction()
<generator object yielderFunction at 0x07742D28>
``````

``````>>> gen = yielderFunction()
>>> dir(gen)
['__class__',
...
'__iter__',    #Returns gen itself, to make it work uniformly with containers
...            #when given to a for loop. (Containers return an iterator instead.)
'close',
'gi_code',
'gi_frame',
'gi_running',
'next',        #The method that runs the function's body.
'send',
'throw']
``````

`gi_code``gi_frame`字段是凍結狀態的存儲位置。通過探索它們`dir(..)`，我們可以確認我們的上述心理模型是可信的。

``````def fib(limit=50):
a, b = 0, 1
for i in range(limit):
yield b
a, b = b, a+b
``````

``````>>> fib()
<generator object fib at 0x7fa38394e3b8>
``````

``````>>> g = fib()
>>> next(g)
1
>>> next(g)
1
>>> next(g)
2
>>> next(g)
3
>>> next(g)
5
``````

``````results = []
for i in fib(30):       # consumes fib
results.append(i)
# can also be accomplished with
results = list(fib(30)) # consumes fib
``````

``````>>> tuple(fib(5))       # consumes fib
(1, 1, 2, 3, 5)
``````

``````f = fib()
``````

Python編譯函數，遇到`yield`關鍵字並簡單地返回一個生成器對象。似乎不是很有幫助。

``````def yielder(value):
""" This is an infinite generator. Only use next on it """
while 1:
print("I'm going to generate the value for you")
print("Then I'll pause for a while")
yield value
print("Let's go through it again.")
``````

``````>>> gen = yielder("Hello, yield!")
``````

``````>>> next(gen) # runs until it finds a yield
I'm going to generate the value for you
Then I'll pause for a while
'Hello, yield!'
``````

``````>>> next(gen) # continues from yield and runs again
Let's go through it again.
I'm going to generate the value for you
Then I'll pause for a while
'Hello, yield!'
``````

`yield`Python中的語句返回一個生成器。Python中的生成器是一個返回continuation的函數（特別是一種coroutine，但continuation代表了更常用的機制來理解正在發生的事情）。

``````def save_file(filename):
def write_file_continuation():
write_stuff_to_file(filename)

check_if_file_exists_and_user_wants_to_overwrite(write_file_continuation)
``````

``````def f():
while True:
yield 4
``````

``````class Generator():
def __init__(self,iterable,generatorfun):
self.next_continuation = lambda:generatorfun(iterable)

def next(self):
value, next_continuation = self.next_continuation()
self.next_continuation = next_continuation
return value
``````

``````def generatorfun(iterable):
if len(iterable) == 0:
raise StopIteration
else:
return (iterable[0], lambda:generatorfun(iterable[1:]))
``````

## 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
``````

## 發電機

``````>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
...    print(i)
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
``````

## 你的代碼解釋了

``````# 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
``````

``````# 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()`方法是一個列表對象方法，它需要一個iterable並將其值添加到列表中。

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

1. 您不需要兩次讀取值。
2. 您可能有很多孩子，並且您不希望它們都存儲在內存中。

## 控制發電機的耗盡

``````>>> class Bank(): # Let's create a bank, building ATMs
...    crisis = False
...    def create_atm(self):
...        while not self.crisis:
...            yield "\$100"
>>> hsbc = Bank() # When everything's 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's 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
...
``````

## Itertools，你最好的朋友

itertools模塊包含操作iterables的特殊函數。 曾經希望復制發電機嗎？ 鏈兩個發電機？ 使用單行分組嵌套列表中的值？ `Map / Zip`而不創建另一個列表？

``````>>> 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)]
``````

## 理解迭代的內在機制

next ”是發送到閉包的消息，由“ iter ”調用創建。

``````Welcome to Racket v6.5.0.3.

-> (define gen
(lambda (l)
(define yield
(lambda ()
(if (null? l)
'END
(let ((v (car l)))
(set! l (cdr l))
v))))
(lambda(m)
(case m
('yield (yield))
('init  (lambda (data)
(set! l data)
'OK))))))
-> (define stream (gen '(1 2 3)))
-> (stream 'yield)
1
-> (stream 'yield)
2
-> (stream 'yield)
3
-> (stream 'yield)
'END
-> ((stream 'init) '(a b))
'OK
-> (stream 'yield)
'a
-> (stream 'yield)
'b
-> (stream 'yield)
'END
-> (stream 'yield)
'END
->
``````

``````>>> def f():
...   yield 1
...   yield 2
...   yield 3
...
>>> g = f()
>>> for i in g:
...   print i
...
1
2
3
>>> for i in g:
...   print i
...
>>> # Note that this time nothing was printed
``````

（我的下面的答案僅從使用Python生成器的角度講，而不是生成器機制底層實現，這涉及堆棧和堆操作的一些技巧。）

（現在我想談談背後的基本原理`generator`，並`iterator`基於我自己的理解。我希望這可以幫助你掌握迭代器和生成器的基本動機。這樣的概念也出現在其他語言中，比如C＃。）

1. OO方法，我們包裝元數據`as a class`。這就是所謂的`iterator`實現迭代器協議（即`__next__()`，和`__iter__()`方法）的人。這也是常見的迭代器設計模式
2. 功能方法，我們包裝元數據`as a function`。這就是所謂的`generator function`。但在引擎蓋下，返回的`generator object`仍然是`IS-A`迭代器，因為它還實現了迭代器協議。

``````def get_odd_numbers(i):
return range(1, i, 2)
def yield_odd_numbers(i):
for x in range(1, i, 2):
yield x
foo = get_odd_numbers(10)
bar = yield_odd_numbers(10)
foo
[1, 3, 5, 7, 9]
bar
<generator object yield_odd_numbers at 0x1029c6f50>
bar.next()
1
bar.next()
3
bar.next()
5
``````

## Grokking`yield`捷徑

1. 在函數的開頭插入行`result = []`
2. `result.append(expr)`替換每個`yield expr`
3. 在函數底部插入一行`return result`
4. 耶 - 沒有更多的`yield`聲明！ 閱讀並找出代碼。
5. 比較功能與原始定義。

## 不要混淆你的Iterables，Iterators和Generators

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

Python執行以下兩個步驟：

1. 獲取`mylist`的迭代器：

調用`iter(mylist)` - >這將返回一個帶有`next()`方法的對象（或Python 3中的`__next__()` ）。

[這是大多數人忘記告訴你的步驟]

2. 使用迭代器循環遍歷項目：

繼續調用從步驟1返回的迭代器上的`next()`方法。將`next()`的返回值賦給`x`並執行循環體。 如果從`next()`引發異常`StopIteration` ，則意味著迭代器中沒有更多值，並且退出循環。

1. 內置列表，詞典，元組，集，文件。
2. 用戶定義的實現`__iter__()`
3. 發電機。

``````def f123():
yield 1
yield 2
yield 3

for item in f123():
print item
``````

## 為什麼要使用發電機？

`yield`關鍵字簡單地收集返回結果。想想`yield`就好`return +=`

``````def getNextLines():
while con.isOpen():
``````

``````for line in getNextLines():
doSomeThing(line)
``````

``````def simpleYield():
yield "first time"
yield "second time"
yield "third time"
yield "Now some useful value {}".format(12)

for i in simpleYield():
print i
``````

``````"first time"
"second time"
"third time"
"Now some useful value 12"
``````

`yield`關鍵字在Python中有什麼作用？

# 回答大綱/摘要

• 帶有`yield`的函數在被調用時返回一個Generator
• 生成器是迭代器，因為它們實現了迭代器協議 ，因此您可以迭代它們。
• 還可以生成器發送信息 ，使其在概念上成為協程
• 在Python 3中，您可以在兩個方向上從一個生成器委派給另一個生成器，其中`yield from`
• （附錄批評了幾個答案，包括最重要的答案，並討論了在發電機中使用`return` 。）

# 發電機：

`yield`在函數定義中只是合法的，並且函數定義中包含`yield`使它返回一個生成器。

`yield`提供了一種實現迭代器協議的簡單方法， 該協議由以下兩種方法定義： `__iter__``next` （Python 2）或`__next__` （Python 3）。 這兩種方法都使對象成為迭代器，您可以使用`collections`模塊中的`Iterator` Abstract Base Class進行類型檢查。

``````>>> 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's an iterable
True
>>> hasattr(gen, 'next')       # and with .next (.__next__ in Python 3)
True                           # implements the iterator protocol.
``````

``````>>> import collections, types
>>> issubclass(types.GeneratorType, collections.Iterator)
True
``````

``````>>> isinstance(gen, types.GeneratorType)
True
>>> isinstance(gen, collections.Iterator)
True
``````

`Iterator`一個特性是，一旦耗盡 ，您就無法重用或重置它：

``````>>> list(gen)
['I am', 'a generator!']
>>> list(gen)
[]
``````

``````>>> list(func())
['I am', 'a generator!']
``````

``````def func(an_iterable):
for item in an_iterable:
yield item
``````

``````def func(an_iterable):
yield from an_iterable
``````

# 協同程序：

`yield`表示允許將數據發送到生成器的表達式（參見腳註3）

``````def bank_account(deposited, interest_rate):
while True:
calculated_interest = interest_rate * deposited

>>> my_account = bank_account(1000, .05)
``````

``````>>> first_year_interest = next(my_account)
>>> first_year_interest
50.0
``````

``````>>> next_year_interest = my_account.send(first_year_interest + 1000)
>>> next_year_interest
102.5
``````

## 合作代表團參與Sub-Coroutine的`yield from`

``````def money_manager(expected_rate):
under_management = yield     # must receive deposited value
while True:
try:
additional_investment = yield expected_rate * under_management
except GeneratorExit:
'''TODO: write function to send unclaimed funds to state'''
finally:
'''TODO: write function to mail tax info to client'''

def investment_account(deposited, manager):
'''very simple model of an investment account that delegates to a manager'''
next(manager) # must queue up manager
manager.send(deposited)
while True:
try:
yield from manager
except GeneratorExit:
return manager.close()
``````

``````>>> my_manager = money_manager(.06)
>>> my_account = investment_account(1000, my_manager)
>>> first_year_return = next(my_account)
>>> first_year_return
60.0
>>> next_year_return = my_account.send(first_year_return + 1000)
>>> next_year_return
123.6
``````

## 其他方法：關閉並拋出

`close`方法在函數執行被凍結時引發`GeneratorExit` 。 這也將由`__del__`調用，因此您可以將任何清理代碼放在處理`GeneratorExit`

``````>>> my_account.close()
``````

``````>>> import sys
>>> try:
...     raise ValueError
... except:
...     my_manager.throw(*sys.exc_info())
...
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
File "<stdin>", line 2, in <module>
ValueError
``````

# 結論

`yield`關鍵字在Python中有什麼作用？

# 附錄：

## 批評最高/已接受的答案**

• 關於什麼使迭代成為混亂，僅使用列表作為示例。 請參閱上面的參考資料，但總結一下：iterable有一個`__iter__`方法返回迭代器迭代器提供了一個`.next` （Python 2或`.__next__` （Python 3）方法，它被`for`循環隱式調用，直到它引發`StopIteration` ，一旦它啟動，它將繼續這樣做。
• 然後它使用生成器表達式來描述生成器是什麼。 由於生成器只是創建迭代器的一種方便方法，因此它只會混淆事情，我們仍然沒有達到`yield`部分。
• 控制生成器耗盡時，他調用`.next`方法，而在`next`他應該使用內置函數。 這將是一個適當的間接層，因為他的代碼在Python 3中不起作用。
• Itertools？ 這與`yield`完全沒有關係。
• 在Python 3中沒有討論`yield`提供的方法以及新功能。 最高/接受的答案是一個非常不完整的答案。

## 對答案的批判表明在生成器表達或理解中的`yield` 。

``````expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
('=' (yield_expr|testlist_star_expr))*)
...
yield_expr: 'yield' [yield_arg]
yield_arg: 'from' test | testlist
``````

CPython核心開發人員正在討論棄用其配額 。 這是郵件列表中的相關帖子：

2017年1月30日19:05，Brett Cannon寫道：

• 3.7中的SyntaxWarning或DeprecationWarning
• 2.7.x中的Py3k警告
• 3.8中的SyntaxError

- Nick Coghlan | ncoghlan at gmail.com | 澳大利亞布里斯班

## 生成器中的`return`語句

An `expression_list`基本上是用逗號分隔的任意數量的表達式 - 實質上，在Python 2中，您可以使用停止生成器`return`，但不能返回值。

## 腳註

1. 提議中引用了CLU，Sather和Icon語言，以便將生成器的概念引入Python。一般的想法是函數可以維持內部狀態並根據用戶的要求產生中間數據點。這承諾在性能上優於其他方法，包括Python線程，在某些系統上甚至不可用。

2. 這意味著，例如，`xrange`對象（`range`在Python 3中）不是`Iterator`s，即使它們是可迭代的，因為它們可以被重用。與列表一樣，它們的`__iter__`方法返回迭代器對象。

3. `yield`最初是作為語句引入的，這意味著它只能出現在代碼塊中一行的開頭。現在`yield`創建一個yield表達式。https://docs.python.org/2/reference/simple_stmts.html#grammar-token-yield_stmt這種變化提出以允許用戶將數據發送到發電機，就像一個會接受它。要發送數據，必須能夠將其分配給某些內容，為此，語句將無效。

`yield`就像一個函數的返回元素。不同之處在於，`yield`元素將函數轉換為生成器。在某些東西“屈服”之前，生成器的行為就像一個函數。發電機停止，直到下一次調用，並從它開始的完全相同的點繼續。您可以通過調用將所有“已產生”值的序列合二為一`list(generator())`

• 我打電話給你，告訴你我想要一個以特定方式產生的數字序列，我告訴你算法是什麼。
該步驟對應於`def`生成器函數，即包含a的函數`yield`
• 過了一會兒，我告訴你，“好的，準備告訴我數字的順序”。
此步驟對應於調用返回生成器對象的生成器函數。請注意，你還沒有告訴我任何數字; 你抓住你的紙和鉛筆。
• 我問你，“告訴我下一個號碼”，你告訴我第一個號碼; 之後，你等我問你下一個號碼。這是你的工作，要記住你在哪裡，你已經說過的數字，以及下一個數字是什麼。我不關心細節。
此步驟對應於調用`.next()`生成器對象。
• ...重複上一步，直到......
• 最終，你可能會走到盡頭。你沒告訴我一個號碼; 你只是喊道，“抓住你的馬！我已經完成了！沒有更多的數字！”
此步驟對應於生成器對象結束其作業，並引發`StopIteration`異常生成器函數不需要引發異常。當函數結束或發出時，它會自動引發`return`

``````for item in sequence:
``````

`return`函數中的A 將返回單個值。

``````import random

def return_dates():
dates = [] # With 'return' you need to create a list then return it
for i in range(5):
date = random.choice(["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th"])
dates.append(date)
return dates
``````

``````def yield_dates():
for i in range(5):
date = random.choice(["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th"])
yield date # 'yield' makes a generator automatically which works
# in a similar way. This is much more efficient.
``````

``````dates_list = return_dates()
print(dates_list)
for i in dates_list:
print(i)

dates_generator = yield_dates()
print(dates_generator)
for i in dates_generator:
print(i)
``````

``````class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
``````