Python基础10——迭代器和生成器

10.1迭代器
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator
迭代器对象要求支持迭代器协议的对象,在Python中,支持迭代器协议就是实现对象的_ iter ()和 next _()的方法。

li = []
for i in range(1,11):     #for后面要是可迭代对象
    li.append(i)
print(li)

li1 = [i for i in range(1,11)]    #列表推导式
print(li1)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

迭代器其实就是一个可迭代对象

>>> li = [1,2,3,4,5,6,7]
>>> n = iter(li)   #将列表li变成了迭代器
>>> n
<list_iterator object at 0x0000000002EFAB70>
>>> type(n)
<class 'list_iterator'>
>>> next(n)
1
>>> next(n)
2
>>> next(n)
3
>>> next(n)
4
>>> next(n)
5
>>> next(n)
6
>>> next(n)
7
>>> next(n)
Traceback (most recent call last):
  File "<pyshell#16>", line 1, in <module>
    next(n)
StopIteration

迭代器其实就是一个容器,里面装有元素,不能直接查看,可以通过next()取出元素,直到把所有元素取出就停止迭代报错。

那既然迭代器是一个容器,那我们可以向从其他容器里取出特定的数据或者倒着取数据吗?
看下面可以看到list有很多方法支持我们从不同位置等特定的需求来取数据,但是我们发现迭代器却没有方法,所以我们无法对迭代器做任何的操作,只能遵从迭代器顺序的方式依次取出数据。

>>> li = [1,2,3,4,5,6,7]
>>> n = iter(li)
>>> dir(n)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']
>>> dir(list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

注意:next(n) == n._ next _() 取出下一个元素
同理:iter(n) == n._ iter _() 返回迭代器对象本身

>>> li = [1,2,3,4]
>>> li = li.__iter__()
>>> li
<list_iterator object at 0x0000000002F774E0>
>>> next(li)
1
>>> li.__next__()
2

先来看下for循环到底发生了什么
li –>iter(li) –>i=next(li)

li = [1,2,3,4,5,6,7,8,9,10]
for i in li:     #li -->iter(li) -->i=next(li)
    print (i)   

插播一个三目元算符:

>>> a = 1
>>> b = 2
>>> c = 3
>>> d  = c if a else b  #如果a!=0,d = c,else d=b 
>>> d
3
>>> a = 0
>>> d = c if a else b
>>> d
2

10.2生成器
生成器都是迭代器对象,都可以使用next()和iter()方法(拥有迭代器类似的属性)。
迭代器和生成器都是容器,迭代器元素确定了,生成器没有确定。

>>> li = [i for i in range(1,11)]
>>> li
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> li = (i for i in range(1,11))  #[]-->>()转换成生成器
>>> li
<generator object <genexpr> at 0x0000000002F64780>
>>> type(li)
<class 'generator'>
#裴波那契数列
def fib(maxnum):     #函数
    n,a,b = 0,0,1
    while n < maxnum:
        print(b)
        a,b = b,a+b
        n += 1

def fib1(maxnum):    #生成器函数用def定义用yield语法糖一次返回一个结果,返回的其实是生成器的迭代器,成为生成器
    n,a,b = 0,0,1
    while n < maxnum: 
        yield b        #yield返回的是一个生成器,运行到yield会停止
        a,b = b,a+b
        n += 1
>>> fib(6)
1
1
2
3
5
8
>>> fib1(6)  #因为fib1(b)利用yield返回了生成器,但是fib1没有传值的话就只是一个函数
<generator object fib1 at 0x0000000003193E60>
>>> type(fib)
<class 'function'>
>>> type(fib1)
<class 'function'>
>>> a = fib1(6)  
>>> a
<generator object fib1 at 0x0000000003193E60>
>>> next(a)
1
>>> next(a)
1
>>> next(a)
2
>>> next(a)
3

看下面这个例子,生成器有个特性,就是遇到生成器程序会中断,next(生成器)就会继续运行下面

def ff():
    print('天才第一步 1')
    yield 2
    print('确实纸尿裤 3')
    yield 4
    print('球王贝利代言 5')
    yield (6)

结果:

>>> c = ff()
>>> c
<generator object ff at 0x0000000002EFFAF0>
>>> next(c)    
天才第一步 1
2
>>> next(c)
确实纸尿裤 3
4
>>> next(c)
球王贝利代言 5
6
>>> next(c)
Traceback (most recent call last):
  File "<pyshell#20>", line 1, in <module>
    next(c)
StopIteration

再看一个例子:

def ff():
    print('天才第一步 1')
    yield 2
    print('确实纸尿裤 3')
    yield 4
    print('球王贝利代言 5')
    yield (6)

def fun(n):
    i = 0
    while i < n:
        print('********',i)
        yield i
        i += 1
        print ('++++',i)


def fff():
    n=1
    while True:
        print(n)
        yield n
        n += 1
>>> fun(6)
<generator object fun at 0x0000000002EFFAF0>
>>> a = fun(6)
>>> a
<generator object fun at 0x0000000002F64888>
>>> next(a)
******** 0
0
>>> next(a)
++++ 1
******** 1
1
>>> next(a)
++++ 2
******** 2
2
>>> next(a)
++++ 3
******** 3
3
>>> next(a)
++++ 4
******** 4
4
>>> next(a)
++++ 5
******** 5
5
>>> next(a)
++++ 6
Traceback (most recent call last):
  File "<pyshell#31>", line 1, in <module>
    next(a)
StopIteration

生成器和迭代器不一样的是,迭代器其实就是一个容器用来存储元素,但是生成器不是用来存储元素的,而是一般用来存储生成元素的算法,然后一边生成一边运行,也就是我们通过生成器一个个去生成元素,而不是一下子生成全部的元素,这样对内存的占用会比较大。
比如我们要生成一个list,因为内存是有限的,列表的容量就是有限的,而且假如我们创建生成了一个元素特别多的list,但是我们只需要访问前面几个元素的话,那后面那些空间就是浪费的,所以这样我们可以利用生成器,一边循环一边计算生成元素,不用创建完整的list,从而节省了大量的空间。

小结:

迭代器:
1. 可以实现next(),iter()方法(next(a) == a._ next _())
2. 迭代器是一个容器,用来保留元素,利用next函数来取出元素,一个取出一个取完报错
3. iter函数返回的是迭代器对象本身,可以使用这个函数来将其他转换成迭代器
4. 创建迭代器的方法:iter函数

生成器:
1. 生成器是一个特殊的迭代器,支持迭代器协议,也就是可以实现next(),iter()方法
2. 生成器也是一个容器,只不过它保存的不是元素而是生成元素的算法,一边生成一边运行
3. 生成器用def定义用yield语法糖来返回生成器(生成器其实是生成器的迭代器)
4. 程序遇到yield就会暂停,然后调用生成器的next()方法返回生成的元素的同时程序会继续往下运行
5、创建生成器的方法:yield语法糖、(i for i in range(1,11))(列表生成器中括号改成小括号 )
6、相比较直接生成一个完整list,生成器占用内存很小

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值