1. 程式人生 > >python全棧學習總結五:叠代器和生成器

python全棧學習總結五:叠代器和生成器

分享圖片 ble 重新 stop 就會 callable 循環 三次 min

一 叠代器

1 什麽是叠代器協議

  叠代器協議:對象必須提供一個next方法,執行該方法要麽返回叠代中的下一項,要麽引起一個Stoplteration異常,以終止叠代(只能往後走不能往前退)

  可叠代對象:實現了叠代器協議的對象(如何實現:對象內部定義一個__iter__()方法)

  協議是一種約定,可叠代對象實現了叠代器協議,python的內部工具(如for循環,sum,min,max函數等)使用叠代器協議訪問對象。

2 自定義叠代器

class MyIterator:
    #自定義叠代器類
    def __init__(self,x = 2,xmax = 63):
        self.
__mul,self.__x = x,x self.__xmax = xmax def __iter__(self): #定義叠代器協議方法,返回類本身 return self def __next__(self):#定義叠代器協議方法 if self.__x and self.__x !=1: self.__mul *= self.__x if self.__mul <= self.__xmax: return self.__mul else
: raise StopIteration else : raise StopIteration if __name__ == __main__: myiter = MyIterator(3,150) #實例化叠代器MyIterator for i in myiter: print("叠代的數據元素為:",i)

3 可叠代對象

  字符串,列表,元組,字典,集合,文件(str,list,tuple,dict,set,file)均為可叠代對象,通過for循環調用對象內部的__iter__方法,變成可叠代對象

  使用的叠代器協議函數

  x = ‘hell‘

  iter_x = x.__iter__() #轉化為可叠代對象,遵循叠代協議

  iter_x.__next__() #轉換後,可以使用__next__()方法來進行叠代

  next(iter_x)#也可以使用next函數進行叠代

  for循環的實質是在調用next方法或函數

  另外兩個叠代函數
  iter(iterable) 參數iterable為可叠代類型

  iter(callable,sentine) 參數callable是可調用類型,一般為函數,第二個參數為哨兵,當第一個參數(函數)調用返回值等於第二個參數的值時,叠代或遍歷停止。

class Counter: #定義用於計數的類
    def __init__(self,x=0): #定義構造函數,初始化實例屬性
        self.x = x
counter = Counter()  #初始化實例類Counter
def used_iter():#定義用於iter()函數的函數
    counter.x +=2 #修改計數類中的實例屬性的值
    return counter.x

for i in iter(used_iter,8):  #叠代iter()函數產生的叠代器
    print("本次遍歷的數值:",i)

4 for和while操作可叠代對象,輸出相同

x = hello
for i in x:
    print(i)

x = hello
x_iter = x.__iter__()
while True:
    try:
        print(x_iter.__next__())
    except StopIteration:
        print("叠代循環完成!")
        break

二 生成器

  生成器:一種數據類型,自動實現了叠代器協議(其他的數據類型需要調用自己內置的__iter__方法),所以生成器就是可叠代對象

1 三元表達式與列表解析

  三元表達式:res = ‘good‘ if num == True else ‘bad‘ #當num為True時,res的結果為‘good’,否則res的結果為‘bad‘

  列表解析:l = [i for i in range(10)] #生成列表l = [0,1,2,3,4,5,6,7,8,9]

        l1 = [i for i in range(10) if i>5] #三元表達式與列表解析相結合。

2 生成器表達式創建

  (1)生成器表達式:num = (i for i in range(10) if i > 5) #註意列表解析是用的[]而生成器表達式是()

#列表解析
ret_list = []
for i in range(10):
    ret_list.append(" 包子%s"%i)
print(ret_list)
ret_list = ["包子%s"%i for i in range(10)]
print(ret_list)
ret_list = ("包子%s"%i for i in range(10))
print(ret_list)
print(ret_list.__next__())
print(ret_list.__next__())
print(ret_list.__next__())

技術分享圖片

註意觀察列表解析和生成器創建的區別:列表解析一次生成列表,而生成器只是創建一個生成器對象,需要調用next方法才能訪問。列表解析數據很多時,需要占用大量的內存。生成器的好處顯而易見,用的時候才生成。

#生成器創建+三元判斷
ret_list = ("包子%s"%i for i in range(10) if i > 5)
print(ret_list)
print(ret_list.__next__())
print(ret_list.__next__())
print(ret_list.__next__())

(2)生成器對象是通過yield關鍵字定義的函數對象,因此,生成器也是一個函數,生成器用於生成一個值的序列,以便在叠代中使用。

def myYiled(n): #定義一個生成器(函數)
    while n>0:
        print("開始生成...:")
        yield  n #yield語句,用於返回給調用者其後表達式的值
        print("完成一次...")
        n -= 1
if __name__ == __main__:
    for i in myYiled(4):
        print("遍歷得到的值:",i)

    print(**10)
    my_yield = myYiled(3)
    print(已經實例化生成器對象!)
    print(my_yield.__next__())
    print(第二次調用__next__()方法)
    print(my_yield.__next__())
    print("第三次開始調用!")
    print(next(my_yield))

運行現象:

技術分享圖片

yield語句是生成器中的關鍵語句,生成器在實例化時並不會立即執行,而是等待調用其__next__()方法的時候才開始執行,並且當程序運行完yield語句後就會保持(hold)住,即保持當前狀態且停止運行,等待下一次遍歷時,才恢復運行!如上圖!!!!

使用生成器,可以生成一個值的序列用於叠代,並且這個值的序列不是一次生成的,而是使用一個,再生成一個,的確可以使程序節約大量內存!

yield語句不僅可以使函數成為生成器和返回值,還可以接收調用者傳來的數值。但值得註意的是:第一次調用生成器不能傳給生成器None以外的值,否則會引發錯誤!使用生成的send方法傳值!

def myYield(n):
    while n >  0:
        rcv = yield  n #rcv用來接收調用者傳來的值
        n -= 1
        if rcv is not None:
            n = rcv
if __name__ == __main__:

    my_yield = myYield(3)
    print(my_yield.__next__())
    print(next(my_yield))

    print(--*20)
    print("傳給生成器一個值,重新初始化生成器!")
    print(my_yield.send(5))
    for i in my_yield:
        print(i)

    my_yield = myYield(3)
    #print(my_yield.send(4))  #xxxx第一次調用就傳值錯誤!!!
    print(my_yield.send(None)) #但是可以傳None
    print(next(my_yield))
    print(my_yield.send(6))

3 生成器特性:只能取一次

利用生成器來計算總人口,從文件中讀取數據,分析出人口的總數量,並計算出所占比例

def get_population():
    with open("人口統計",r,encoding=utf-8) as f:
        for p in f:
            yield p
num_p = get_population()
# print(num_p.__next__())
# print(next(num_p))
all_p = sum(int(eval(p)[population]) for p in num_p)  #eval提取數據類型字典
print("總人口:%s"%all_p)
num_p = get_population()#上面已經取完,重新調用生成器
for num in num_p:
    num_dict = eval(num)
    # print("%s的比例:%s%%"%(10,8))
    print("%s的比例:%.2f%%"%(num_dict[name],int(num_dict[population])*100/all_p))

#人口統計文件
{"name":"濟南","population":"100"} {"name":"青島","population":"200"} {"name":"煙臺","population":"300"} {"name":"濰坊","population":"400"} {"name":"菏澤","population":"500"} {"name":"臨沂","population":"600"}

上面例程中,調用內置函數sum求和,使用生成器的方式計算,沒有使用列表解析的方式,節省了大量內存,同理使用max,min等類似!一定要用生成器的方法!一個一個的計算!

4 生成器與協程(生產者與消費者)

運用send()方法來重置生成器的生成序列,也成為協程,是一種解決程序並發的方法!

def consumer(): #定義一個消費者模型(生成器協程)
    print("等待接收處理任務...")
    while True:
        data = (yield ) #模擬接收並處理任務
        print(收到任務!) #此處可以執行函數調用來完成相關任務

def producer():
    c = consumer()
    c.__next__()
    for i in range(5):
        print("發送一個任務...‘,‘任務%d"%i)
        c.send(任務%d%i)

if __name__ == __main__:
    producer()

python全棧學習總結五:叠代器和生成器