二十四、迭代器与生成器

迭代器与生成器

一、迭代器

容器

  • 在python中一切都是对象,对象的抽象就是就是类,对象的集合就是容器。

可迭代的对象

  • 字符串 元组 列表 字典 集合 类 构成的数据都是一种容器。容器之间的区别在于内部数据结构的实现方法

迭代器

  • 迭代器是访问容器的一种方式。
  • 是一个可以记住遍历的位置的对象。
  • 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退

可迭代对象通过iter()函数返回一个迭代器,在通过next()函数就可以实现遍历。我们之前学习的for in 其实把这个过程隐式化了。它的原理就是这样的。

  • iter() 创建迭代器  
  • next() 迭代一个元素

如下演示的是一个列表的迭代过程

data = [1,2,3,4]
die_dai_qi = iter(data) # 创建一个迭代器 迭代的对象是一个列表

print(next(die_dai_qi)) # 每次只迭代一个元素
print(next(die_dai_qi))
print(next(die_dai_qi))
print(next(die_dai_qi))
print(next(die_dai_qi)) # 当元素迭代完成 迭代器结束 再次迭代报错


/usr/local/bin/python3.7 /Users/zhonglinglong/PycharmProjects/whole_world/临时文件.py
Traceback (most recent call last):
  File "/Users/zhonglinglong/PycharmProjects/whole_world/临时文件.py", line 18, in <module>
    print(next(die_dai_qi))
StopIteration
1
2
3
4

Process finished with exit code 1

如下演示的是迭代一个类的过程

  • 把一个类作为一个迭代器使用需要在类中实现两个方法 __iter__() 与 __next__() 
  • 迭代类对象的过程实际上是迭代__iter__()的值,在下一次迭代时 会先执行 __next__() 
class Demo:
    def __iter__(self):
        self.a = 1
        return self

    def __next__(self):
        x = self.a
        self.a += 1
        return x

die_dai_qi = iter(Demo())
print(next(die_dai_qi))
print(next(die_dai_qi))
print(next(die_dai_qi))
print(next(die_dai_qi))
print(next(die_dai_qi))
print(next(die_dai_qi))
print(next(die_dai_qi))

二、生成器

生成器其实是一种特殊的迭代器。它的本质就是一个返回迭代器的函数 。

  • (i for i in range(10000))  小阔号阔起来的也是一个生成器用来遍历生成的数
  • 同样是求和。迭代器永远都是先生成数据。然后在迭代。而生成器则是在求和的时候。要一个数字则迭代一个。然后舍弃一个。所以它的内存变化几乎为0。如果数据量太的时候。这种差距非常非常巨大。
# 显示当前 python 程序占用的内存大小
def show_memory_info(hint):
    pid = os.getpid()
    p = psutil.Process(pid)
    info = p.memory_full_info()
    memory = info.uss / 1024. / 1024
    print('{} 程序占用内存: {} MB'.format(hint, memory))


def test_iterator():
    show_memory_info('未生成迭代器之前占用的内存')
    list_1 = [i for i in range(10000)]
    show_memory_info('使用迭代器之后占用的内存')
    print(sum(iter(list_1)))
    show_memory_info('求和之后占用的内存')


def test_generator():
    show_memory_info('未生成生代器之前占用的内存')
    list_2 = (i for i in range(10000))
    show_memory_info('生成生代器之后占用的内存')
    print(sum(list_2))
    show_memory_info('求和之后占用的内存')

test_iterator()
test_generator()


/usr/local/bin/python3.7 /Users/zhonglinglong/PycharmProjects/whole_world/临时文件.py
未生成迭代器之前占用的内存 程序占用内存: 47.8984375 MB
使用迭代器之后占用的内存 程序占用内存: 48.234375 MB
49995000
求和之后占用的内存 程序占用内存: 48.234375 MB
未生成生代器之前占用的内存 程序占用内存: 48.234375 MB
生成生代器之后占用的内存 程序占用内存: 48.234375 MB
49995000
求和之后占用的内存 程序占用内存: 48.234375 MB

Process finished with exit code 0
  •  一个函数出出现了yield 函数那么该函数就是一个生成器。它返回的是个生成器
  • 我们看下generator函数。它返回的就是一个生成器(查看打印结果)  因为有yield函数存在。它的作用简单的理解成。当调用函数generator 程序执行到yield 函数暂停 直接跳出程序。不过如果它就这个作用那就没有意义了。它从这里跳出。是为了等待next()函数来跳出。 第一次执行next(generator(1)) 的结果就是yield 后面跟着的语句值并暂停。 再次调用next函数 先执行 yield下面的语句直到再次遇到yield 返回该值然后暂停。而且generator函数里面的局部变量i 并没有因为执行过一次而清除。而是在每次next执行的时候跟着变化

def generator(k):
    i = 1
    while True:
        yield i ** k
        i += 1

gen_1 = generator(1)
gen_3 = generator(3)
print(gen_1)
print(gen_3)


def get_sum(n):
    sum_1, sum_3 = 0, 0
    for i in range(n):
        next_1 = next(gen_1)
        next_3 = next(gen_3)
        print('next_1 = {}, next_3 = {}'.format(next_1, next_3))
        sum_1 += next_1
        sum_3 += next_3
    print(sum_1 * sum_1, sum_3)

get_sum(8)


/usr/local/bin/python3.7 /Users/zhonglinglong/PycharmProjects/whole_world/临时文件.py
<generator object generator at 0x11739a7c8>
<generator object generator at 0x11739a750>
next_1 = 1, next_3 = 1
next_1 = 2, next_3 = 8
next_1 = 3, next_3 = 27
next_1 = 4, next_3 = 64
next_1 = 5, next_3 = 125
next_1 = 6, next_3 = 216
next_1 = 7, next_3 = 343
next_1 = 8, next_3 = 512
1296 1296

Process finished with exit code 0

综述对比迭代器和生成器。

  • 生成器在使用数据的时候才生成。不会造成过大的内存压力
  • 迭代器是一个有限集。因为在你生成数据的时候就已经指定了迭代次数。然而生成器是无限集。因为它返回的是一个迭代器的函数。你只管调next()。生成器根据运算会自动生成新的元素然后在返回给你

给一个列表,和值。求值在列表中的下标。如下演示代码通过正常写法和用生成器的写法(要注意的是。生成器返回的是一个对象。要list转化成列表) 

def index_normal(L, target):
    result = []
    for i, num in enumerate(L):
        if num == target:
            result.append(i)
    return result

print(index_normal([1, 6, 2, 4, 5, 2, 8, 6, 3, 2], 2))




def index_generator(L, target):
    for i, num in enumerate(L): # 把可迭代的序列组合成索引和值。类似于字典的item
        if num == target:
            yield i # 条件满足的时候生成器追加一个元素

print(list(index_generator([1, 6, 2, 4, 5, 2, 8, 6, 3, 2], 2)))


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bug来袭~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值