Python基础-day14-迭代器

"""
任何数据类型,只要含有__iter__方法,那它就是可迭代的。
    这就是 可迭代协议
任何数据类型,只要含有__iter__和__next__方法,那它就是迭代器。
    这就是 迭代器协议
"""
from collections import Iterable
from collections import Iterator


class A:
    def __iter__(self):
        pass

    def __next__(self):
        pass


a = A()
print(isinstance(a, Iterable))
print(isinstance(a, Iterator))

迭代器有一个__next__方法,调用迭代器的__next__方法,我们可以获取迭代器中的值。

每调用一次,就获取一个值,直到取完。

之所以能对一个对象使用for循环,是因为这个对象是可迭代的,也就是说,当一个对象含有__iter__时,就能用for循环。

 

通过编写生成器,我们自己也能实现迭代器。(生成器的本质是迭代器)

创建生成器的方式1:生成器函数。

    # 一个函数只要含有yield关键字,那么这个函数就是生成器函数。

    # return返回值后,函数结束。

    # yield返回值后,函数不结束,而是停在那里。

    # yield不能和return共用。

def generator():
    print(1)
    yield 'a'


ret = generator()
print(ret)

打印:

<generator object generator at 0x02AF9F30>

这说明了:含有yield的函数就是生成器函数,因此返回了一个对象。

 

def generator():
    print(1)
    yield 'a'


ret = generator()
value = ret.__next__()  # 调用一次,生成器就走一步,遇到yield后就停下脚步,返回的'a'交给了value
print(ret)
print(value)

打印结果为:

1

<generator object generator at 0x03979F30>

a

 

def generator():
    print(1)
    yield 'a'
    print(2)
    yield 'b'


ret = generator()
value1 = ret.__next__()
value2 = ret.__next__()
print(value1, value2)

打印结果:

1

2

a b

每执行一次__next__,代码就走一下,这一下包括打印1,返回'a',然后就停止不动,直到再次调用__next__。

 

使用for循环:

def generator():
    print(1)
    yield 'a'
    print(2)
    yield 'b'


ret = generator()
for i in ret:
    print(i)

打印:

1

a

2

b

这就是利用for来遍历生成器对象,从而打印结果。(为什么这里的i就代表了1和'a'呢,而前面的__next__必须要用一个变量来接收返回值'a'呢?

 

小功能:

"""
实时监听文件的输入
"""


def tail(filename):
    f = open(filename, encoding='utf-8')
    while True:
        line = f.readline()

        if line and line != '\n':
            print(line.strip())


tail('test.log')

这种方式很耗资源,在while循环中,它无限循环,给资源造成了很大的压力,这种循环读取文件的方式太暴力了。

比较好的方式是:当文件修改了,才去读取文件,而不是有事无事都跑去问,这多累啊。

这就遇到一个问题:是否改变,也是需要不断的去检查,这也是会涉及到无限循环?

能否用阻塞的形式呢?也就是如果文件没有变化,那么就阻塞在那里,代码不执行,一旦变化,再执行。

 

双下划线方法:很少直接调用,一般情况下,是通过其他语法触发的。

比如__next__是通过for循环触发的。

比如__add__是通过 + 符号触发的。

 

调用可迭代对象的__iter__方法,就能得到一个迭代器。

ret = 可迭代对象.__iter__

这里的ret就是一个迭代器。

 

迭代器的特点:

一个迭代器只能从头到尾取一次,不能重复取,不能往回取,这样可以节省内存空间。

 

 

 

对于迭代器,我们有三种方法来获取它里面的值

 

 

 for循环和__next__,前面已经说明了。

ret = generator()
li = list(ret)  # 利用list()强制转换迭代器对象
print(li)

返回结果:

1

['a']

 

现在再来介绍send():

send的作用同__next__。

只不过,代码在开始执行前,会先接受send发来的值。接收的位置就是上一次暂停的地方。

如上图所示。

 

 

yield from:

 

 

创建生成器的方式2:生成器表达式。

 

 

 

 

 

 

{10: 'a', 34: 'b'}

 

都是在内部先遍历,然后再做一些处理,最后才得出结果。

跟我们写逻辑是一样的,只不过推导式是简化了的方法。

 

 

 

 

列表推导式

字典推导式

集合推导式

生成器推导式

唯独元祖没有推导式

 

代码要写的易懂且简洁,这需要很深的功底。

 

posted @ 2019-11-02 15:16  道霖  阅读(165)  评论(0)    收藏  举报