菜鸟初学python入门进阶第八节:面向对象,迭代器和生成器

1.python中的迭代协议与迭代器

迭代器是访问集合内元素的一种方式,一般用来遍历数据
迭代器是不会重复的,只会一条一条访问完数据
迭代器提供了一种惰性访问数据的方式
迭代协议__iter__

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

__slots__是限制类能添加的属性的,详情:https://blog.csdn.net/qq_37616069/article/details/79688781

迭代器
一个指着容器的指针
继承了Iterable

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

Iterator.register(bytes_iterator)
Iterator.register(bytearray_iterator)
#Iterator.register(callable_iterator)
Iterator.register(dict_keyiterator)
Iterator.register(dict_valueiterator)
Iterator.register(dict_itemiterator)
Iterator.register(list_iterator)
Iterator.register(list_reverseiterator)
Iterator.register(range_iterator)
Iterator.register(longrange_iterator)
Iterator.register(set_iterator)
Iterator.register(str_iterator)
Iterator.register(tuple_iterator)
Iterator.register(zip_iterator)

主要还是在__next__上,获取下一个元素,迭代器其实已经重载了iter
list实现了iter,所以是一个可迭代类型,但不是迭代器
迭代器必须实现next方法

a = []
iter_rator = iter(a)
print(isinstance(iter_rator, Iterator))
True

iter函数可以给容器分配一个迭代器(已经可以不用写了)

from _collections_abc import *

class a:
    def __init__(self, ls):
        self.ls = ls

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

aa = a([1, 2, 3, 4, 5])
print(isinstance(aa, Iterable))
print(isinstance(aa, Iterator))
for item in aa:
    print(item)
    '''当用这种方式调用,会先尝试类里的__iter__方法,如果没有,会先创建默认迭代器,再调用__getitem__'''
b = iter(aa)
print(isinstance(b, Iterable))
print(isinstance(b, Iterator))
for item in b:
    print(item)
False
False
1
2
3
4
5
True
True
1
2
3
4
5

自定义迭代器:

from _collections_abc import *

class Myitor(Iterator):
    def __init__(self, ls):
        self.ls = ls
        self.index = 0
        self.len = len(ls)

    def __next__(self):
        if self.index == self.len:
            return 'over'
        w = self.ls[self.index]
        print('index', self.index)
        self.index += 1
        return w

a = [1, 2, 3, 4]
pointer = Myitor(a)
while True:
    print(next(pointer))
    '''指针往后指一位'''

2.生成器函数的使用

只要函数里有yield关键字,就是生成器函数
生成器函数返回一个生成器对象
在python编译字节码时候产生
生成器函数也是对象,且实现了迭代协议,所以其实例(返回的值)可以用for循环访问

import time

def gen_func():
    for i in range(5):
        '''可多次yield到调用包'''
        yield round(time.time())
        time.sleep(1)

gen = gen_func()

for value in gen:
    print(value)
1595003121
1595003122
1595003123
1595003124
1595003125

这让我们实现协程、惰性求值、延迟求值成为可能

生成器斐波那契:

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

gen = gen_func(3)

for value in gen:
    print(value)

3.生成器原理

python.exe是用c写的,且会用c语言函数PyEval_EvalFramEx去执行python的函数
调用函数时首先会创建一个栈帧stack frame
python中一切皆对象,栈帧也是对象,把代码变成字节码对象

import dis

def foo():
    bar()
def bar():
    pass

print(dis.dis(foo))
 41           0 LOAD_GLOBAL              0 (bar)
              2 CALL_FUNCTION            0
              4 POP_TOP
              6 LOAD_CONST               0 (None)
              8 RETURN_VALUE
None

当调用子函数,又会创建一个栈帧,在新栈帧里运行字节码
所有栈帧都分配在堆内存上,堆内存不释放就会一直在内存中
这是python可以动态修改代码的基础,静态语言函数执行完立即释放内存
python函数的栈帧可以独立于调用者存在(反正都在内存中,只要有指针指着就行)

import inspect
frame = None
def foo():
    bar()
def bar():
    global frame
    frame = inspect.currentframe()
    '''获取栈帧'''

foo()
print(frame.f_code.co_name)
super_func = frame.f_back
print(super_func.f_code.co_name)
bar
foo

生成器函数也可以有return,但是每yield一次就有一个标记(字节码多一行),使得可以查看这个函数的上一个yield标记的值
生成器对象是对生成器的一层封装
在这里插入图片描述
在这里插入图片描述

4.生成器例子

当我们对类用for item in class:来遍历,类里没有实现iter时,会生成默认迭代器再去找getitem。
for循环这种遍历方式的实现是

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

其中用了生成器和getitem实现
for循环实际是iter的体现,用来获取yield标记的值

5.生成器阅读大文件

一行带分隔符的数据,切成一条一条数据

def myreadlines(f, tic):
    buf = "" 
    '''一块一块文件读'''
    while True:
        '''一段一段数据读'''
        while tic in buf:
            pos = buf.index(tic)
            yield buf[:pos]
            buf = buf[pos+len(tic):]
        chunk = f.read(4096)# 往后在读4096byte
        if not chunk:
            '''读到末尾'''
            yield buf
            break
        
        buf += chunk

with open("") as f:
    for a_data in myreadlines(f, "{|}"):
        print(a_data)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值