1. 程式人生 > 實用技巧 >第八章:Python高階程式設計-迭代器和生成器

第八章:Python高階程式設計-迭代器和生成器

第八章:Python高階程式設計-迭代器和生成器

Python3高階核心技術97講 筆記

目錄

  • 第八章:Python高階程式設計-迭代器和生成器
    • 8.1 Python中的迭代協議
    • 8.2 什麼是迭代器和可迭代物件
    • 8.3 生成器函式的使用
    • 8.4 Python是如何實現生成器的?
    • 8.5 生成器在UserList中的應用
    • 8.6 生成器如何讀取大檔案

8.1 Python中的迭代協議

"""
什麼是迭代協議
迭代器是什麼? 迭代器是訪問集合內元素的一種方式, 一般用來遍歷資料
迭代器和以下標的訪問方式不一樣, 迭代器是不能返回的, 迭代器提供了一種惰性方式資料的方式
[] list , __iter__ __next__
"""
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


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

8.2 什麼是迭代器和可迭代物件

from collections.abc import Iterator

class Company(object):
    def __init__(self, employee_list):
        self.employee = employee_list

    def __iter__(self):
        return MyIterator(self.employee)

    # def __getitem__(self, item):
    #     return self.employee[item]


class MyIterator(Iterator):
    def __init__(self, employee_list):
        self.iter_list = employee_list
        self.index = 0

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

if __name__ == "__main__":
    company = Company(["tom", "bob", "jane"])
    my_itor = iter(company)
    # while True:
    #     try:
    #         print (next(my_itor))
    #     except StopIteration:
    #         pass

    # next(my_itor)
    for item in company:  # 執行了iter(company)
        print (item)

8.3 生成器函式的使用

# 生成器函式,函式裡只要有yield關鍵字
# 惰性求值,延遲求值提供了可能


def gen_func():  # 返回的是一個生成器物件,在Python編譯位元組碼是產生
    yield 1
    yield 2
    yield 3
    

def fib(index):
    if index <= 2:
        return 1
    else:
        return fib(index-1) + fib(index-2)
    

def fib2(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


def gen_fib(index):
    n,a,b = 0,0,1
    while n<index:
        yield b
        a,b = b, a+b
        n += 1
        

for data in gen_fib(10):
    print (data)
# print (gen_fib(10))
# 斐波拉契 0 1 1 2 3 5 8
#惰性求值, 延遲求值提供了可能

def func():
    return 1


if __name__ == "__main__":
    #生成器物件, python編譯位元組碼的時候就產生了,
    gen = gen_func()
    for value in gen:
        print (value)
    # re = func()
    # pass

8.4 Python是如何實現生成器的?

#1.python中函式的工作原理
"""

"""
import inspect
frame = None
def foo():
    bar()
def bar():
    global frame
    frame = inspect.currentframe()

#python.exe會用一個叫做 PyEval_EvalFramEx(c函式)去執行foo函式, 首先會建立一個棧幀(stack frame)
"""
python一切皆物件,棧幀物件, 位元組碼物件
當foo呼叫子函式 bar, 又會建立一個棧幀
所有的棧幀都是分配在堆記憶體(不會立即釋放)上,這就決定了棧幀可以獨立於呼叫者存在
"""
# import dis
# print(dis.dis(foo))

foo()
print(frame.f_code.co_name)
caller_frame = frame.f_back
print(caller_frame.f_code.co_name)


def gen_func():
    yield 1
    name = "bobby"
    yield 2
    age = 30
    return "imooc"

import dis
gen = gen_func()
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)

8.5 生成器在UserList中的應用

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

from collections import UserList  # 不要用繼承list,因為是c寫的

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

8.6 生成器如何讀取大檔案

#500G, 特殊 一行
def myreadlines(f, newline):
  buf = ""
  while True:
    while newline in buf:
      pos = buf.index(newline)
      yield buf[:pos]
      buf = buf[pos + len(newline):]
    chunk = f.read(4096)

    if not chunk:
      #說明已經讀到了檔案結尾
      yield buf
      break
    buf += chunk

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