python(17): 迭代器 生成器 可迭代对象 容器

容器

容器就是一个用来存储多个元素的数据结构,常见的容器包括【列表】、【元组】、【字典】、【集合】、【字符串】

容器有两个特点:

  1. 容器中的元素可通过迭代获取

    2. 所有容器中的元素被存储在内存中。

可迭代对象

实现了__iter__方法的对象就叫做可迭代对象。

直观理解就是能用for循环进行迭代的对象就是可迭代对象。

如:字符串,列表,元祖,字典,集合等等,都是可迭代对象。

迭代器:

迭代器创建方法1:

iter():将可迭代对象转为迭代器

import sys

mylist1 = [1, 3, 4, 6, 7, 8, 'osaiduhgfoiuydsfhguy', 'adsuygfydshgf', '654856465', 88, 90, 344]
print('==========mylist1=======')
print('type:',type(mylist1))
print(len(mylist1))
print('size:',sys.getsizeof(mylist1))  # 输出list占用内存空间

myiter1 = iter(mylist1)  # 将list转为迭代器
print('==========mylist1=======')
print('type:',type(myiter1))
print('size:',sys.getsizeof(myiter1))  # 输出list占用内存空间

# 从迭代器取值
print('next取值---: ')
print(next(myiter1))
print(next(myiter1))
print('for 取值---:')
for i in myiter1:
    print(i)


》》》》》
==========mylist1=======
type: <class 'list'>
12
size: 152
==========mylist1=======
type: <class 'list_iterator'>
size: 48
next取值---: 
1
3
for 取值---:
4
6
7
8
osaiduhgfoiuydsfhguy
adsuygfydshgf
654856465
88
90
344

迭代器创建方法2:

任何实现了__iter__() 和 __next__()方法的对象都是迭代器。

__iter__() 返回迭代器自身,

__next__() 决定迭代规则;返回容器中的下一个值,如果容器中没有更多元素了,则抛出StopIteration异常。而且,迭代器不会一次性吧所有元素都加载到内存,而是需要的时候才返回结果。 

调用next()方法的时候返回容器中的下一个值,

迭代器每次调用next()方法的时候做两件事:

  1. 为下一次调用next()方法修改状态。
  2. 生成当前调用的返回结果。
class Myiterator():
    # 迭代器求斐波那契数列
    def __init__(self,max):
        self.prev = 0
        self.curr = 1
        self.max = max

    def __iter__(self):
        return self

    def __next__(self):
        if self.max == 0:
            raise StopAsyncIteration
        value = self.curr
        self.curr += self.prev
        self.prev = value
        self.max = self.max-1
        return value

mm = Myiterator(20)
for i in mm:
    print(i)

为什么list、dict、str等数据类型不是Iterator?

这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

缺点

在上面的介绍中也提到了迭代器的缺点,集中说一下:

  1. 取值不够灵活。next方法只能往后取值,不能往前。取值不如按照索引的方式灵活,不能取指定的某一个值
  2. 无法预测迭代器的长度。迭代器通过next()方法取值,并不能提前知道要迭代出的个数
  3. 用完一次就失效

误区

迭代器的优势和缺点已经说的清晰了,现在讨论一个普遍对迭代器的一个误区:迭代器是不能节省内存的
给这句话加一个前提:这里的迭代器是指普通的迭代器,而非生成器,因为生成器也是一种特殊的迭代器。

生成器:

使用了 yield 的函数被称为生成器(generator)

生成器是一种特殊的迭代器。特殊在我们可以通过send()方法像生成器中传入数据,而迭代器只能将数据输出。

生成器一定是迭代器(反之不成立)

其主要的特点有:

1、拥有yield关键字的函数就是生成器函数生成器拥有迭代器的迭代传出数据的功能,但用关键字yield来替代迭代器中的__next__()方法来实现。

2、生成器可以传入数据使用send()进行计算(不同于迭代器),并根据变量内容计算结果后返回。

3、生成器不会一次把所有的元素加载到内存,而是调用的时候才生成返回结果(这点相同于迭代器)

4、可以通过for循环进行迭代(因为生成器是迭代器)

总结:生成器是一种特殊的迭代器,其具有传入数据的功能。

从生成器取值方法:

1. for i in myiter   #形式迭代取值     ----  同迭代器

2. next(myiter)   #迭代器next函数取值  ----  同迭代器

3. myiter.send(x) 

#生成器send函数发送参数,同时取到值(生成器函数中未对接收参数进行任何处理时,也能使用send进行传参)

注:不能在第一次取值时使用send(x);第一次取值使用next()  或者send(None)或for中迭代取值

否则会出现报错:

TypeError: can't send non-None value to a just-started generator-python

python两种方式提供生成器:

一.生成器函数

  1. 使用yield语句而不是return语句返回结果
  2. yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重新在挂起的地方继续执行。函数中可以有多个yield,每个yield都会挂起一次

    3.在for 循环中迭代,或使用next(iterobject,defalt)函数

1.1不带参数

def fib(max):
    n, a = 0, 0
    while n < max:
        print('in while start')
        yield a   #每次返回值则停在此处等待下次取值再往下执行
        a = a+1
        n = n + 1
        print('in while end')

dd = fib(10)
i = 0
while i<10:
    print(next(dd))
    print('完成一次取值')
    i = i +1

>>>>
in while start
0
完成一次取值
in while end
in while start
1
完成一次取值
in while end
in while start
2
完成一次取值
in while end
in while start
3
完成一次取值
in while end
in while start
4
完成一次取值

2.对传入参数进行处理

Send()

实际上next()和send()在一定意义上作用是相似的,区别是send()可以传递yield表达式的值进去,而next()不能传递特定的值,只能传递None进去。因此,我们可以看做c.next() 和 c.send(None) 作用是一样的。

1:def test():
2:    i = 1
3:    while i < 5:
4:        temp = yield i**2       #python程序碰到 “=” 从右往左执行
5:        print("in iter:",temp)
6:        i += 1
7:aa = test()
8:for j in range(4):
9:    if j == 0:
10:       print(aa.send(None))
11:   else:
12:       print(aa.send(j))
13:   print('==========')

》》》》
1                  #第一次执行到第4行等号右侧
==========
in iter: 1         #第二次执行 从第4行等号左侧接收send参数 往下执行
4
==========
in iter: 2
9
==========
in iter: 3
16
==========

解析:

1.第一次执行只执行到yield右侧 并返回yield右侧的值

2.send() 把参数传递给temp,并让生成器执行一次到yield右侧,接收返回值

二.生成器表达式:类似于列表推导式,但是,生成器返回按需要产生结果的一个队想,而不是一次构建整个结果列表

列表推导式: squares = [x**2 for x in range(5)]   #squres = [0,1,4,9,16]

换成圆括号--生成器: squares = (x**2 for x in range(5))

next(squares)  # 0

next(squares)  # 1

使用方法

启动:生成器的启动需要用next(xx),或xx.send(None)

优点:Python使用生成器对延迟操作提供了支持.所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果.

  1. 节省内存:当需要创建一个包含一百万元素的列表时,使用列表定义是不现实的,列表元素可以用算法推算出来后,节省大量内存空间,适合使用生成器。

使用场景

  1. 数据的数据规模巨大,内存消耗严重
  2. 数列有规律,但是依靠列表推导式描述不出来
  3. 协程。生成器和协程有着千丝万缕的联系

迭代器与生成器区别

  1. 生成器属于迭代器,但是生成器可以传参数(send()),迭代器不行
  2. 迭代器是一个对象,需要定义迭代器类来实例化。
  3. 生成器是一个对象,用带yield的函数返回

参考:

Python中的可迭代对象,迭代器与生成器 - THESONG - 博客园

一篇文章讲清楚迭代器和生成器 - 金色旭光 - 博客园

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值