Python迭代器和生成器

· 迭代器

什么是迭代器:
迭代器可以让我们访问集合的时候变得非常方便。之前我们通过for…in…来访问一个集合的时候,就是使用迭代器完成的。
如果没有迭代器,那么我们只能通过while循环,每次循环的时候通过下标来访问了。
可迭代对象:
可以直接使用for循环遍历的对象,成为可迭代的对象,常见的可迭代对象有:list、tuple、dict、set、str以及生成器
更加专业的判断一个对象是否是可迭代对象:这个对象有一个__iter__方法,并且这个方法会返回一个迭代器对象,这种对象就叫做可迭代对象。
判断一个对象是否可迭代:
可以使用is instance()判断一个对象是否是interable对象

from collections import Iterable

# 列表是一个可迭代对象
ret = isinstance([1, 2, 3], Iterable)
print(ret)

字符串是一个可迭代对象

ret = isinstance('abc', Iterable)
print(ret)

整形不是一个可迭代对象

ret = isinstance(123, Iterable)
print(ret)

迭代器:
1、在python2中,实现了next和__iter__方法,并且在这个方法中返回了值的对象,叫做迭代器对象
2、在python3中,实现了__next__方法和__iter__方法,并且在这个方法中返回了值的对象,叫做迭代器对象
3、如果迭代器没有返回值了,那么应该在next或者是__next__方法中抛出一个StopInteration
使用iter()方法获取可迭代对象的迭代器:
有时候我们拥有了一个可迭代的对象,我们想要获取这个迭代器,那么可以通过iter(x)方法获取这个可迭代对象
的迭代器。
自定义迭代器:

分开的写法(方法A):

class MyRangeIterator:
    """
    迭代器
    """
    def __init__(self, start, end):
        self.index = start
        self.end = end

    def __iter__(self):
        return self

    def __next__(self):
        if self.index < self.end:
            temp = self.index
            self.index += 1
            return temp
        else:
            raise StopIteration()


class MyRange:
    """
    MyRange是可迭代对象
    """
    def __init__(self, start, end):
        self.start = start
        self.end = end

    def __iter__(self):
        # 这个方法中返回一个迭代器对象
        return MyRangeIterator(self.start, self.end)


if __name__ == '__main__':
    myrange = MyRange(1, 10)
    for i in myrange:
        print(i)

    iterator = iter(myrange)
    while True:
        try:
            x = iterator.__next__()
            print(x)
        except StopIteration:
            break

合并到一起的写法(方法B):

# -*- coding: UTF-8 -*-

from collections import Iterable, Iterator

# Interable: 判断是否可迭代
# Interator: 判断是否是迭代器

print(isinstance('abc', Iterable))
print(isinstance('abc', Iterator))

class MyRange:

    def __init__(self, start, end):
        self.index = start
        self.end = end

    def __iter__(self):
        # 这个方法要返回一个迭代器
        return self

    def __next__(self):
        if self.index < self.end:
            temp = self.index
            self.index += 1
            return temp
        else:
            raise StopIteration()

if __name__ == '__main__':
    myrange = MyRange(1, 10)
    for i in myrange:
        print(i)
    for i in myrange:
        print(i)

以上两个方法,A和B都实现了迭代器的功能,当我们实例化MyRange时,即获得了一个可迭代对象,在方法A中这个可迭代对象返回了一个迭代器:MyRangeIterator(self.start, self.end),方法B中返回了自己self本身。这时就会有一个区别产生,因为A方法在每次返回迭代器的时候,都会实例化迭代
器,而B中只会实例化一次,因此:

myrange = MyRange(1, 10)
for i in myrange:
    print(i)
for i in myrange:
    print(i)

如果两个方法都执行上述代码,A方法每次都会遍历出1到9,而B方法只会遍历一次,第二次什么都不会打印。因为第二次self.index 已经等于了 self.end。

· 生成器
为什么需要生成器:
当我们需要打印一个1到1亿的整形的时候,如果我们采用普通的方式,直接调用range函数,程序会崩溃掉。因为range(1,100000000)函数会直接产生一个从1-1亿的列表,这个列表中所有数据都是存放在内存中的,会导致内存爆满。这时候我们可以采用生成器来解决该问题,生成器不会一次把所有数据都加载到内存中,而是在循环的时候临时产生的,循环一次生成一个,所以程序在运行期间永远都只会生成一个数据,从而节省内存的开销。
next函数和__next__方法:
next函数可以迭代生成器的返回值
自己写生成器:
生成器可以通过函数产生。如果在一个函数中出现了yield表达式,那么这个函数将不再是一个普通的函数,而是一个生成器函数。yield一次返回一个结果,并且会冻结当前函数的状态。

普通的列表

num_list = [x for x in range(1, 100)]
print(num_list)

生成式

num_gen = (x for x in range(1, 100))
print(num_gen)
print(type(num_gen))

for i in num_gen:

print(i)

def my_gen():
yield 1
yield 2
yield 3

ret = my_gen()
print(next(ret))
print(next(ret))
print(next(ret))

print(next(ret))

def gen(start, end):
index = start
while index <= end:
yield index
index += 1

生成器有两个身份:迭代器和可迭代的对象, 因此,只能被遍历一次

ret = gen(1, 100000000)
for i in ret:
print(i)
send方法:
1、也是用来触发代码,直接碰到yield表达式
2、如果用send方法执行刚刚开始的生成器,那么应该传递None给send方法
def my_gen(start):
while start < 10:
# 如果通过next函数执行yield
# 那么yield xxx 永远都是返回None
temp = yield start
print(temp)
start += 1

ret = my_gen(1)

第一次使用send方法必须传None

print(ret.send(None))
print(next(ret))
print(ret.send(‘hello’))

1

None

2

hello

3

send方法和next函数的区别:
1、send方法可以传递值给yield表达式,而next不可以
2、在第一次执行生成器代码的时候,必须要传None,next则不需要
生成器的一些案例:

def fib(count):
    index = 1
    a, b = 0, 1
    while index <= count:
        yield b
        c = b
        b = a + b
        a = c
        index +=1
for x in fib(7):
    print(x)

#使用生成器 来实现多线程,听音乐和看电影同时进行
def netease_music(duration):
    time = 0
    while time <= duration:
        print('音乐听了%d分钟' % time)
        time += 1
        yield None
    # raise StopIteration()

def youku_movie(duration):
    time = 0
    while time <= duration:
        print('电影看了%d分钟' % time)
        time += 1
        yield None
    # raise StopIteration()


if __name__ == '__main__':
    music_iter = netease_music(10)
    movie_iter = youku_movie(30)
    music_stop = False
    movie_stop = False
    while True:
        try:
            next(music_iter)
        except StopIteration:
            print('音乐听完了')
            music_stop = True
        try:
            next(movie_iter)
        except StopIteration:
            print('电影看完了')
            movie_stop = True
        if music_stop and movie_stop:
            break
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值