迭代器、可迭代对象、生成器

可迭代对象

可迭代协议

我们现在是从结果分析原因,能被for循环的就是“可迭代的”,但是如果正着想,for怎么知道谁是可迭代的呢?

假如我们自己写了一个数据类型,希望这个数据类型里的东西也可以使用for被一个一个的取出来,那我们就必须满足for的要求。这个要求就叫做“协议”。

可以被迭代要满足的要求就叫做可迭代协议。*可迭代协议的定义非常简单,就是内部实现了iter方法。*

如果这个对象中有_ iter _()方法,这个对象就是可迭代对象

if '__iter__' in dir(str)

通俗易懂 :可以被for循环迭代的对象就是可迭代对象。

isinstance()判断一个对象是否是Iterable对象:

>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False

迭代器

迭代器是什么

迭代器只不过是一个实现迭代器协议的容器对象。它基于两个方法:

  • next 返回容器的下一个项目
  • _ iter _ 返回迭代器本身
i = iter('asd')

next(i)
>>>'a'

next(i)
>>>'s'

next(i)
>>>'d'

next(i)

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

StopIteration

当序列便利完时,将抛出一个StopIteration异常。这将使迭代器与循环兼容,因为它们将捕获这个异常以停止循环。要创建定制的迭代器,可以编写一个具有next方法的类

为什么使用迭代器

  1. “流式”数据处理方式*少内存消耗

例如: 当处理文件时,把所有数据全部取出来放到内存里面进行处理会导致程序消耗大量内存

一般我们会一部分一部分的对文件内容进行处理:

for text_line in open("xx.txt"):  
    print text_line  

open("xx.txt")返回的是可迭代的对象,所以,可以渐进式地对文件的内容进行处理,即按行来读取文件,并进行处理,而不是,

直接把全部文件一下加载到内存中。
  1. 支持方便用for语句对数据进行处理

python内置的一些常见的像类型像数组、列表甚至字符串等都是可迭代类型,

这样我们就能方便for语句这个语法方便对数据进行消费,不需要自己记录索引位置,人肉循环:

for i in [1,2,3,4]:
    print(i)

迭代器的优点

  • 很方便使用,且只能取所有的数据取一次
  • 节省内存空间

迭代器的内部

可迭代对象

对象里面包含__iter()__方法的实现,对象的iter函数经调用之后会返回一个迭代器,里面包含具体数据获取的实现

迭代器

包含有next方法的实现,在正确范围内返回期待的数据以及超出范围后能够抛出StopIteration的错误停止迭代。

迭代器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。

生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator

把list、dict、str等Iterable变成Iterator可以使用iter()函数:

迭代协议

必须拥有_iter_方法和_next_方法。

迭代器和可迭代对象

正式的说法:

一个实现了iter方法的对象是可迭代的,一个实现next方法的对象则是迭代器

个人理解:

仅实现了iter方法的是可迭代对象,可迭代对象可以通过._ iter _()方法转成迭代器。

迭代器有next 和 _ iter _方法 只要存在这两个方法的都是迭代器。迭代器是一个存储的容器,每当调用next方法的时候才会将数据拿出。并且只执行一次

即迭代器可以作为一个数据生成器

生成器

生成器的本质就是迭代器

迭代器 我们知道是用来迭代可迭代对象的,而生成器是用来迭代方法的

我们知道的迭代器有两种:一种是调用方法直接返回的,一种是可迭代对象通过执行iter方法得到的,迭代器有的好处是可以节省内存。

如果在某些情况下,我们也需要节省内存,就只能自己写。我们自己写的这个能实现迭代器功能的东西就叫生成器。

生成器Generator:

  本质:迭代器(所以自带了iter方法和next方法,不需要我们去实现)

  特点:惰性运算,开发者自定义

1.生成器函数:

常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行

只要有yield的关键字的函数就是生成器函数

一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于returnreturn的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束。
特点:
调用函数的之后函数不执行,返回一个生成器
每次调用next方法的时候会取到一个值
直到取完最后一个,在执行next会报错
从生成器中取值的几个方法
# next
# for
# 数据类型的强制转换 : 占用内存
例1:

1)遍历文件把文件名返回,并进行计算文件的大小

import os
def traverse(dir):
    if os.path.isdir(dir):
        files = os.listdir(dir)
        for file in files[::-1]:
            full_name = os.path.join(dir,file)
            traverse(full_name)
    else:
        yield dir
调用
for i in traverse(r'C:\'):
    print(os.getsize(i))
例2:

监听文件

import time

def tail(filename):
    f = open(filename)
    f.seek(0, 2) #从文件末尾算起
    while True:
        line = f.readline()  # 读取文件中新的文本行
        if not line:
            time.sleep(0.1)
            continue
        yield line
调用
tail_g = tail('tmp')
for line in tail_g:
    print(line)
例3预激协程的装饰器

计算移动平均值

def init(func):
    def inner(*args,**kwargs):
        g = func(*args,**kwargs)
        next(g)
        return g
    return inner

@init
def averager():
    total = 0.0
    count = 0
    average = None
    while 1:
        trem = yield average
        total += term
        count += 1
        average = total / count
调用
g_avg = averager()
print(g_avg.send(10))
print(g_avg.send(30))
print(g_avg.send(5))
理解
我们都知道 生成器函数只有当调用next方法时,才会进入函数内部进行执行,为了方便调用 将next放到装饰器中,这样使调用更方便、易懂

当然我们就上述代码也可以不写装饰器 而写成
g_avg = averager()
next(g_avg)
print(g_avg.send(10))
print(g_avg.send(30))
print(g_avg.send(5))

但是试想,如果有N多生成器函数,我们需要N多next()方法,语句看起来也会混乱
yield from
def gen1():
    for c in 'AB':
        yield c
    for i in range(3):
        yield i

print(list(gen1()))

def gen2():
    yield from 'AB'
    yield from range(3)

print(list(gen2()))

2.生成器表达式:

类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表

列表生成式 生成的结果是一个列表
语法:
[x for x in range(10)]                  从数组中依次将内容取出
相当于

x = []
for i in range(10):
    x.append(i)

[x * 2 for x in range(10)]                  从数组中依次将内容取出,并进行计算
相当于

x = []
for i in range(10):
    x.append(i * 2)

[x for x in range(10) if x % 2 == 0]            从数组中 将 符合条件 的内容取出
相当于

x = []
for i in range(10):
    if i % 2 ==0:
        x.append(i)

[x * 2 for x in range(10) if x % 2 == 0]        从数组中 将 符合条件 的内容取出, 并计算
相当于

x = []
for i in range(10):
    if i % 2 ==0:
        x.append(i * 2)

[x * y for x in range(10) for y in range(10)]   循环嵌套 
相当于 

x = []
for x in range(10):
    for y in range(10):
        x.append(x * y)

[x % 2 == 0 for x in range(10)]

for x in range(10):
    if x % 2 == 0:
        return True
    else:
        return False
例一:

30以内所有能被3整除的数

[i for i in range(30) if i % 3 is 0]
例二:

30以内所有能被3整除的数的平方

[squared(i) for i in range(30) if i % 3 is 0]
例三:

找到嵌套列表中名字含有两个‘e’的所有名字

names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
         ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]

print([name for lst in names for name in lst if name.count('e') >= 2])
字典推导式
例一:

将一个字典的key和value对调

mcase = {'a': 10, 'b': 34}
mcase_frequency = {mcase[k]: k for k in mcase}
print(mcase_frequency)
例二:

合并大小写对应的value值,将k统一成小写

mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
mcase_frequency = {k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0) for k in mcase.keys()}
print(mcase_frequency)
集合推导式
例:

计算列表中每个值的平方,自带去重功能

squared = {x**2 for x in [1, -1, 2]}
print(squared)
# Output: set([1, 4])

当然 也可以传递方法。列表推导式的用法十分广泛,还可以配合python的内置函数进行使用如 sum max 等

总结

1.把列表解析的[]换成()得到的就是生成器表达式

2.列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存

3.Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。

  • 8
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值