1. 程式人生 > >迭代器和生成器的一些注意問題

迭代器和生成器的一些注意問題

一、迭代器

可迭代的(可迭代物件):
可迭代物件都是可迭代的。如:str、list、dict、tuple、檔案物件等等
只要是物件有__iter__內部方法,就可以稱之為可迭代物件

迭代器:
迭代器也是可迭代的,他不光有__iter__方法,還有__next__方法。
迭代器一次只能取一個值,直到取完後引發一個錯誤
獲得一個迭代器:
呼叫可迭代物件的__iter__()就可以獲得迭代器
使用迭代器:

  • 呼叫迭代器的__next__()方法
  • 使用for迴圈

迭代器的特點:

  • 惰性運算
  • 從前到後一次去取值,過程不可逆 不可重複
  • 節省記憶體
如何判斷一個變數是不是迭代器或者可迭代的
方法一:
print('__iter__'
in dir([1,2,3,4])) print('__next__' in dir([1,2,3,4])) 方法二: from collections import Iterable from collections import Iterator print(isinstance([1,2,3,4],Iterable)) str_iter = 'abc'.__iter__() print(isinstance(str_iter,Iterator)) print(isinstance('abc',Iterable))

二、生成器

生成器的本質就是迭代器,它有迭代器的所有特點,只不過生成器是自己編寫的python 程式碼

1、生成器函式

生成器函式和普通函式之間的區別:

  • 生成器函式中含有yield關鍵字
  • 生成器函式呼叫的時候不會立即執行,而是返回一個生成器
def g_func():
    print('aaaa')
    yield 1
    print('bbbb')
    yield 2
    yield 3

g = g_func()
for i in g:
    print(i)
print(g.__next__())
print(g.__next__())
print(g.__next__())
def cloth():
    for i in range(1000000):
        yield
'衣服%s'%i g = cloth() for i in range(50): print(g.__next__()) for i in range(50): print(g.__next__())

2、sned用法(進階)

def func():
    print('*'*10)
    a = yield 5
    print('a : ',a)
    yield 10
g = func()
num = g.__next__()
# print(num)
num2 = g.send('alex')
num2 = g.send('aaaa')
print(num2)

**在下面的例子中,send會在”yield average”處返回average,並將10傳遞到”yield average”處
**

def averager():
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield average
        total += term
        count += 1
        average = total/count
g_avg = averager()
g_avg.__next__() ---》 先使用一次生成器,返回的值是None
print(g_avg.send(10)) ---》 傳遞的值是10,然後執行term =10,執行到第二次迴圈的yield average時候,返回average,這裡列印的是第一次迴圈中的average = total/count計算出的值
print(g_avg.send(30)) ---》 從term = 處執行程式碼,然後把下一次迴圈的average返回,然後暫停
print(g_avg.send(20))
print(g_avg.send(100))
print(g_avg.send(200))

生成器的預激裝飾器
在使用send的時候,需要先使用一次next方法,具體因素參照上面的例子

def init(func):  #生成器的預激裝飾器
    def inner(*args,**kwargs):
        g = func(*args,**kwargs)   #func = averager
        g.__next__()
        return g
    return inner

@init
def averager():
    total = 0.0
    count = 0
    average = None
    term = yield average
    total += term
    count += 1
    average = total/count
    yield average
g_avg = averager()
print(g_avg.send(10))
print(g_avg.send(30))

3、總結

  • 生成器函式:生成一個生成器的函式
  • 生成器的本質引數迭代器
  • 生成器函式的特點:
    • 帶有yield關鍵字
    • 且呼叫之後,函式內的程式碼不執行
  • 觸發執行的方式:
    • next
    • send :send(None) == __next__(),send在next的基礎上傳一個值到生成器函式內部(send操作不能用在生成器使用的第一次)
    • for迴圈

三、生成器表示式

1、引子

(1)、列表推導式

例一:30以內所有能被3整除的數

>>> [i for i in range(30) if i%3 == 0]
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]

例二:30以內所有能被3整除的數的平方

>>> [i**2 for i in range(30) if i%3 == 0]
[0, 9, 36, 81, 144, 225, 324, 441, 576, 729]

例三:找到巢狀列表中名字含有兩個‘e’的所有名字

names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
         ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
>>> [j for i in names for j in i if j.count('e') == 2]
['Jefferson', 'Wesley', 'Steven', 'Jennifer']

(2)、字典推導式

例一:將一個字典的key和value對調

>>> dic = {'a': 10, 'b': 34}
>>> {dic[k]:k for k in dic}
{10: 'a', 34: 'b'}
>>> 

例二:合併大小寫對應的value值,將k統一成小寫


>>> dic= {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
>>> {k.lower():dic.get(k.lower(),0) + dic.get(k.upper(),0)for k in dic}
{'a': 17, 'b': 34, 'z': 3}

(3)、集合推導式

例:計算列表中每個值的平方,自帶去重功能

>>> {i**2 for i in [1,-1,2]}
{1, 4}

2、生成器表示式

g = (i*i for i in y)
for i in g:
    print(i)