python生成器

生成器

1、yield關鍵字

yield的英文單詞意識就是生產,在函數中但凡出現yield關鍵字,在調用函數,就不會繼續執行函數體代碼,而是會返回一個值。編程

def func():
    print(1)
    yield
    print(2)
    yield
g = func()
print(g)

#輸出:# generator 生成器
<generator object func at 0x0000019C5C5BD448>

生成器 的本質就是迭代器,同時也並不只僅是迭代器,不過迭代器以外的用途實在是很少,因此咱們能夠認爲:生成器提供了很是方便的自定義迭代器的途徑。而且從python2.5+開始,[PEP 342:經過加強生成器實現協同程序的實現位生成器加入了更多的特性,這意味着,生成器還能夠完成更多的工做。這部分咱們會在稍後的部分介紹。多線程

def func():
    print('from func 1')
    yield 'a'
    print('from func 2')
    yield 'b'

g = func()
print(g.__iter__())
print(g.__iter__()==g)
res1 = g.__next__()
print(res1)
res2 = next(g)
print(res2)

#輸出:
<generator object func at 0x00000172C20B2DC8>
True
from func 1
a
from func 2
b

二 、 協同程序

協同程序(協程)通常來講是指這樣的函數:併發

  • 彼此間有不一樣的局部變量、指令指針,但仍共享全局變量;
  • 能夠方便地掛起、恢復,而且有多個入口點和出口點;
  • 多個協同程序間表現爲協做運行,如A的運行過程當中須要B的結果才能繼續執行。

協程的特色決定了同一時刻只能有一個協同程序正在運行(忽略多線程的狀況)。得益於此,協程間能夠直接傳遞對象而不須要考慮資源鎖、或是直接喚醒其餘協程而不須要主動休眠,就像是內置了鎖的線程。在符合協程特色的應用場景,使用協程無疑比使用線程要更方便。函數

從另外一方面說,協程沒法併發其實也將它的應用場景限制在了一個很狹窄的範圍,這個特色使得協程更多的被拿來與常規函數進行比較,而不是與線程。固然,線程比協程複雜許多,功能也更強大,因此我建議你們緊緊地掌握線程便可,是否是聽了一臉懵逼,那麼就別管他了,由於併發編程你會從新學習他。所以這一節裏我也就不列舉關於協程的例子了,如下介紹的方法瞭解便可。學習

因爲Python2.5+對生成器的加強實現了協程的其餘特色,在這個版本中,生成器加入了以下方法:線程

2.1 send(value):指針

send是除next外另外一個恢復生成器的方法。Python2.5+中,yield語句變成了yield表達式,這意味着yield如今能夠有一個值,而這個值就是在生成器的send方法被調用從而恢復執行時,調用send方法的參數。code

def h():
    print('--start--')
    first = yield 5  # 等待接收 Fighting! 值
    print('1', first)
    second = yield 12  # 等待接收 hahaha! 值
    print('2', second)
    yield 13
    print('--end--')


g = h()
first = next(g)  # m 獲取了yield 5 的參數值 5
# (yield 5)表達式被賦予了'Fighting!',  d 獲取了yield 12 的參數值12
second = g.send('Fighting!')
third = g.send('hahaha!')  # (yield 12)表達式被賦予了'hahaha!'
print(f'--over--')
print(f"first:{first}, second:{second}, third:{third}")
--start--
1 Fighting!
2 hahaha!
--over--
first:5, second:12, third:13
  • 調用send傳入非None值前,生成器必須處於掛起狀態,不然將拋出異常。不過,未啓動的生成器仍可使用None做爲參數調用send。
  • 若是使用next恢復生成器,yield表達式的值將是None。

2.2 close()協程

這個方法用於關閉生成器。對關閉的生成器後再次調用next或send將拋出StopIteration異常。

def repeater():
    n = 0
    while True:
        n = (yield n)


r = repeater()
r.close()
print(next(r))  # StopIteration

2.3 throw(type, value=None, traceback=None)

中斷Generator是一個很是靈活的技巧,能夠經過throw拋出一個GeneratorExit異常來終止Generator。Close()方法做用是同樣的,其實內部它是調用了throw(GeneratorExit)的。咱們看close的源代碼

def close(self):
    try:
        self.throw(GeneratorExit)
    except (GeneratorExit, StopIteration):
        pass 
    else:
        raise RuntimeError("generator ignored GeneratorExit") # Other exceptions are not caught

3、自定義range()方法

def my_range(start, stop, step=1):
    while start < stop:
        yield start
        start += 1


g = my_range(0, 3)
print(f"list(g): {list(g)}")
list(g): [0, 1, 2]

4、總結

  1. 提供一種自定義迭代器的方式
  2. yield能夠暫停住函數,並提供當前的返回值

yield和return:

  1. 相同點:二者都是在函數內部使用,均可以返回值,而且返回值沒有類型和個數的限制
  2. 不一樣點:return只能返回一次之;yield能夠返回屢次值

5、生成器表達式

  • 把列表推導式的[]換成()就是生成器表達式
  • 優勢:省內存,一次只產生一個值在內存中
t = (i for i in range(10))
print(t)
print(f"next(t): {next(t)}")
<generator object <genexpr> at 0x1101c4888>
next(t): 0

5.1 生成器表達式和列表推導式

# 生成器表達式
with open('52.txt', 'r', encoding='utf8') as f:
    nums = [len(line) for line in f]

print(max(nums))
#輸出:
1
# 列表推導式
with open('52.txt','r',encoding='utf8') as f:
    nums = (len(line) for line in f)

print(max(nums)) # ValueError: I/O operation on closed file.
相關文章
相關標籤/搜索