python迭代器和生成器

python迭代協議

1.迭代協議: 可迭代類型 Iterable 迭代器iterator

2.什麼是迭代器:
迭代器是用來訪問集合內元素的一種方式,一般用來遍歷數據
迭代器和下標的訪問方式不一樣,迭代器是不能返回的,迭代器提供了一種惰性數據的方式
[] list , __ iter __

#from _collections_abc import Iterator,Iterable
from collections.abc import Iterator,Iterable
#list
a=[1,2,3,4]
print(isinstance(a,Iterable)) #True
print(isinstance(a,Iterator)) #False
#如果list實現了iter()並且返回了Iterator1迭代器,
#那麼Iterator就是一個迭代器
Iterator1=iter(a)
print(isinstance(Iterator1,Iterator))#True

源碼:
Iterable:


class Iterable(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterable:
            return _check_methods(C, "__iter__")
        return NotImplemented


Iterator:


class Iterator(Iterable):

    __slots__ = ()

    @abstractmethod
    def __next__(self):
        'Return the next item from the iterator. When exhausted, raise StopIteration'
        raise StopIteration

    def __iter__(self):
        return self

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterator:
            return _check_methods(C, '__iter__', '__next__')
        return NotImplemented

自定義迭代器

下面這段代碼是將類的對象變爲可迭代對象,然後通過iter(實例化對象)
返回一個迭代器。

class Company(object):
    def __init__(self,employee_list):
        self.employee=employee_list
    '''
    如果函數中_iter_和__getitem__都沒有實現,
    則在使用iterator=iter(類的實例化對象)會報出錯誤
    '''
    def __iter__(self):
        return iter(self.employee)
    # def __getitem__(self, item):
    #     return self.employee[item]
if __name__=="__main__":
     company=Company(["xiaopang","xiaohe","like"])
     my_itor=iter(company)
     for   item in company:
         print(item)
 

自定義迭代器:


class Company(object):
    def __init__(self,employee_list):
        self.employee=employee_list
    '''
    如果函數中_iter_和__getitem__都沒有實現,
    則在使用iterator=iter(類的實例化對象)會報出錯誤
    '''
    def __iter__(self):
        return MyIterator(self.employee)


from collections.abc import Iterator
class MyIterator:   #迭代器是在需要的使用
    def __init__(self,employee_list):
        self.iter_list=employee_list
        #維護內部的一個變量
        self.index=0
    #__iter__在這個自定義的迭代器中可不用實現
    # def __iter__(self):
    #     return self

    def __next__(self):
        #真正返回迭代值的邏輯
        try:
             word=self.iter_list[self.index]
        except  IndexError:
             raise  StopIteration
        self.index+=1
        return word

if __name__=="__main__":
     company=Company(["xiaopang","xiaohe","like"])
     my_itor=iter(company)
     # for   item in company:
     #     print(item)
     while True:
         try:
             print(next(my_itor))
         except StopIteration:
             pass

     pass

生成器函數的使用

#生成器函數  函數裏只要存在  yield關鍵字
def  gen_func():
    yield 1
    #返回的是一個生成器對象,生成器對象是在python
    #編譯字節碼的時候就產生了
    yield 2
    yield 3
#傳統做法
def   fib(index):
    if index<=2:
        return 1
    else :
        return fib(index-1)+fib(index-2)
print(fib(10))


def  fib1(index):
    re_list=[]
    n,a,b=0,0,1
    while n<index:
        re_list.append(b)
        a,b=b,a+b
        n+=1
    return re_list
print(fib1(10))
#斐波那契    0 1  2    3   5  8   13 21 34  55
#惰性求值延遲求值提供了可能
#使用生成器實現斐波那契
def  gen_fib(index):
    n,a,b=0,0,1
    while n<index:
        yield b
        a,b=b,a+b
        n+=1
print("...........生成器..........")
for   data  in gen_fib(10):
      print(data)

print(".......................")
def func():
    return 1
if __name__=="__main__":
    gen=gen_func()
    #生成器對象也是實現了我們的迭代器協議的
    for value in gen:
        print(value)
    re=func()
    pass

生成器原理

1.python函數運行原理:


#python在運行之前會編譯,變異成字節碼文件
#python中函數的工作原理


import inspect
frame=None
def  foo():
    bar()

def bar():
  global   frame
  frame=inspect.currentframe()


# python解釋器python.exe使用c語言來寫的,解釋器會用
#一個叫做PyEval_EvalFramEx() 的c語言函數去執行python
#函數foo,首先會創建一個棧幀(stack frame)
"""
python中一切皆對象,棧幀是一個對象,會將python中的對象
變爲字節碼對象  當foo調用子函數bar,又會創建一個棧幀,並將
解釋器的控制權交給這個棧幀對象,然後將bar變爲字節碼對象


所有的棧幀都是分配在堆內存上,堆內存如果不去釋放它,它
就會一直存放在堆內存當中,這就決定了棧幀可以獨立於調用
者存在

"""
# import  dis
# #查看函數foo的字節碼
# print(dis.dis(foo))

'''
查看foo函數的字節碼過程:
  6           0 LOAD_GLOBAL              0 (bar)
              2 CALL_FUNCTION            0
              4 POP_TOP
              6 LOAD_CONST               0 (None)
              8 RETURN_VALUE
None
'''
foo()
# 這個函數調用完之後我們依然可以拿到它的棧幀
print(frame.f_code.co_name) #bar
#可以拿到調用bar的棧幀
caller_frame=frame.f_back
print(caller_frame.f_code.co_name)#foo

在這裏插入圖片描述
在這裏插入圖片描述
2.生成器

生成器像上述一樣運行在堆內存當中,並且獨立於調用者存在,完成了對函數整個運行過程的控制。yield生成器對象可以讓函數暫停,並訪問到f_lasti和f_locals。



def  gen_fun():
    yield 1
    name="xiaopang"
    yield 2
    age =23
    return "eater"
import dis
gen=gen_fun()   #生成器運行在堆內存中
print(dis.dis(gen))

print(gen.gi_frame.f_lasti)
print(gen.gi_frame.f_locals)
next(gen)
#
print(gen.gi_frame.f_lasti)
print(gen.gi_frame.f_locals)
next(gen)
print(gen.gi_frame.f_lasti)
print(gen.gi_frame.f_locals)


'''
 53           0 LOAD_CONST               1 (1)
              2 YIELD_VALUE
              4 POP_TOP

 54           6 LOAD_CONST               2 ('xiaopang')
              8 STORE_FAST               0 (name)

 55          10 LOAD_CONST               3 (2)
             12 YIELD_VALUE
             14 POP_TOP

 56          16 LOAD_CONST               4 (23)
             18 STORE_FAST               1 (age)

 57          20 LOAD_CONST               5 ('eater')
             22 RETURN_VALUE
None
'''

生成器在UserList當中的應用

class company:
       def getitem(self, item):
                pass

list源碼用c語言寫的,看不到它的實現,
可以通過from collections import UserList查看python實現
from collections import UserList
from _collections_abc import MutableSequence,Sequence

Sequence源碼:

class Sequence(Reversible, Collection):

    """All the operations on a read-only sequence.

    Concrete subclasses must override __new__ or __init__,
    __getitem__, and __len__.
    """

    __slots__ = ()

    @abstractmethod
    def __getitem__(self, index):
        raise IndexError

    def __iter__(self):
        i = 0
        try:
            while True:
                v = self[i]
                yield v
                i += 1
        except IndexError:
            return

    def __contains__(self, value):
        for v in self:
            if v is value or v == value:
                return True
        return False

    def __reversed__(self):
        for i in reversed(range(len(self))):
            yield self[i]

    def index(self, value, start=0, stop=None):
        '''S.index(value, [start, [stop]]) -> integer -- return first index of value.
           Raises ValueError if the value is not present.
        '''
        if start is not None and start < 0:
            start = max(len(self) + start, 0)
        if stop is not None and stop < 0:
            stop += len(self)

        i = start
        while stop is None or i < stop:
            try:
                v = self[i]
                if v is value or v == value:
                    return i
            except IndexError:
                break
            i += 1
        raise ValueError

    def count(self, value):
        'S.count(value) -> integer -- return number of occurrences of value'
        return sum(1 for v in self if v is value or v == value)

Sequence.register(tuple)
Sequence.register(str)
Sequence.register(range)
Sequence.register(memoryview)


使用生成器讀取大文件的一個應用

1.準備input.txt
只有1行數據
{|}作爲分隔符
文件不大作爲測試用。

    bfksabcdkbaklbndklsnbklankldnklsnfklsnfsnflns{|}春花秋月{|}The Raspberry Pi Foundation是英國一個小型的慈善組織,成立的宗旨在於推廣科技,而非以銷售技術來營利。該基金會{|}過去從來沒真的發表過一款產品,因而選擇了兩家全球渠道商e絡盟和RS Components爲其處理首批Raspberry Pi訂單。面對{|}的是業餘愛好者和熱心DIY 的科技迷,Raspberry Pi銷售非常不錯。{|}Raspberry Pi是一款針對電腦業餘愛好者、教師、小學生以及小型企業等用戶的迷你電腦,預裝Linux系統,體積僅信用{|}卡大小,搭載ARM架構處理器,運算性能和智能手機相仿{|}在接口方面,Raspberry Pi提供了可供鍵鼠使用的USB接口,此外還有快速以太網接口、SD卡擴展接口以及1個HDMI高{|}清視頻輸出接口,可與顯示器或者TV相連{|}在查raspberry pi B版針腳定義的時候發現了r-pi短短的一{|}年時間已經經歷了4代了,下面就把我搜集的幾個版本圖片分享給大家,簡單說說各個版本的差別{|}最早誕生時,測試版的raspberry pi誕生於英國 倫敦國王大學 King’s College London {|}簡稱KCL 該學校有完善的電子開發環境,包括自己印製電路板,所以在最初版的raspberry {|}pi在樹莓派Logo左側有該電路板的編號 KCL-8-94V-0 ,板子所用零件都是雜七雜八非生產{|}物料提供,最初版的實物照片只在官方論壇的一些內部用戶帖子中出現,市面沒有流通{|}正式面世量產的第一版和它非常相向,基本零件規格使用上沒有差別,只是插接件{|}貼片元件均使用工廠批量生產的規格,用料好,板子脫去了beta版的青澀,變得成熟性感。{|}正式第一版上市後各大媒體開始瘋狂報道,樹莓派的隊伍也越來越壯大{|}球Geek的擺弄,很早就有了超頻的內核出現,直到2012-8-16開始,官方也提供了超頻內核,並開放{|}了config.txt用於自定義超頻,不過,這是在發佈第二版之後才官方開放的。

2.讀取的代碼:



#讀取大文件500G  文件只有一行
# f=open()
# f.readline()和f.readlines()這兩個
# 都是直接將所有內容一次性直接放到內存當中的

'''
f.read()很多人認爲它是一次性將文件讀入到我們的內存當中,實際上
這裏面如果我們傳遞一個參數數字200它就只會讀出200個字符,如果我
們接着繼續f.read(300),它就會接着我們上一次的偏移量進行讀取,這個
偏移量對象內部已經維護好了,我們不需要維護它之前讀過的偏移量。
我們只需要反覆的多次調用f.read()就可以完成繼續讀取數據。
'''
 #             文件對象 分隔符
def myreadlines(f,newline):
    #聲明一個緩存 存儲已經讀出來的數據
    buf=""
    while True:

        '''
        這個while循環使用來處理f.read(4096*10)有可能讀取數行數據
        則會包含多個分隔符,這個while循環不斷進行判斷是否只有一個
        分隔符
        '''
        #查詢 緩存中是否包含了分隔符
        while newline in buf:
            #找出分隔符在緩存字符串的位置
            pos=buf.index(newline)
            #使用生成器將已緩存的數據返回
            yield buf[:pos]
            #取出分隔符後面的數據
            buf=buf[pos+len(newline):]
        chunk=f.read(4096*10)
        #說明已經讀到了文件的結尾
        if not chunk:
            yield buf
            break
        buf+=chunk


with open("input.txt") as f:
    for  line in myreadlines(f,"{|}"):
        print(line)


"""
打印結果:
H:\ANACONDA\python.exe I:/ainlp/pythonHight/chapter09/readFile.py
bfksabcdkbaklbndklsnbklankldnklsnfklsnfsnflns
春花秋月
The Raspberry Pi Foundation是英國一個小型的慈善組織,成立的宗旨在於推廣科技,而非以銷售技術來營利。該基金會
過去從來沒真的發表過一款產品,因而選擇了兩家全球渠道商e絡盟和RS Components爲其處理首批Raspberry Pi訂單。面對
的是業餘愛好者和熱心DIY 的科技迷,Raspberry Pi銷售非常不錯。
Raspberry Pi是一款針對電腦業餘愛好者、教師、小學生以及小型企業等用戶的迷你電腦,預裝Linux系統,體積僅信用
卡大小,搭載ARM架構處理器,運算性能和智能手機相仿
在接口方面,Raspberry Pi提供了可供鍵鼠使用的USB接口,此外還有快速以太網接口、SD卡擴展接口以及1個HDMI高
清視頻輸出接口,可與顯示器或者TV相連
在查raspberry pi B版針腳定義的時候發現了r-pi短短的一
年時間已經經歷了4代了,下面就把我搜集的幾個版本圖片分享給大家,簡單說說各個版本的差別
最早誕生時,測試版的raspberry pi誕生於英國 倫敦國王大學 King’s College London 
簡稱KCL 該學校有完善的電子開發環境,包括自己印製電路板,所以在最初版的raspberry 
pi在樹莓派Logo左側有該電路板的編號 KCL-8-94V-0 ,板子所用零件都是雜七雜八非生產
物料提供,最初版的實物照片只在官方論壇的一些內部用戶帖子中出現,市面沒有流通
正式面世量產的第一版和它非常相向,基本零件規格使用上沒有差別,只是插接件
貼片元件均使用工廠批量生產的規格,用料好,板子脫去了beta版的青澀,變得成熟性感。
正式第一版上市後各大媒體開始瘋狂報道,樹莓派的隊伍也越來越壯大
球Geek的擺弄,很早就有了超頻的內核出現,直到2012-8-16開始,官方也提供了超頻內核,並開放
了config.txt用於自定義超頻,不過,這是在發佈第二版之後才官方開放的。
"""



完結

下一篇 python socket 編程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章