Python学习:迭代器与生成器

1.生成器

如果创建一个有很多元素的列表,但是只需要访问前几个元素,后面的元素占着的空间就白白浪费了

在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。

在Python中,这种一边循环一边计算的机制,称为生成器:generator。

要创建一个generator,有很多种方法

第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

L = [x * x for x in range(10)]
g = (x * x for x in range(10))
print(L)#
print(g)

输出

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
<generator object <genexpr> at 0x0000029277CA6AC0>

可以直接打印出list的每一个元素,但怎么打印出generator的每一个元素

print(next(g))
print(next(g))

输出

0
1

如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:

generator保存的是算法,比如我要知道计算第四次的值,next要调用四次,这太麻烦了

for n in g:
    print(n)

输出

0
1
4
9
16
25
36
49
64
81

如果要计算很多次,不断调用next实在是太变态了,正确的方法是使用for循环,generator也是可迭代对象,如果某些算法用列表生成式的for循环无法实现,还可以用函数,比如著名的斐波拉契数列(Fibonacci)

def fib(max):
    n,a ,b = 0,0,1#n是用来控制迭代的次数
    while n < max:
        yield b
        a,b = b,a+b
        n = n+1
    return 'done'

如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:

函数是顺序执行,遇到return语句或者最后一行函数语句就返回

在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行

def add():
    print('step 1')
    yield 1
    print('step 2')
    yield (3)
    print('step 3')
    yield (5)

在调用该generator时,首先要生成一个generator对象,然后用next()函数不断获得下一个返回值:

o = add()
print(next(o))
print(next(o))
print(next(o))

输出

step 1
1
step 2
3
step 3
5

通过next调用时,遇到yield就停下了,再次调用在中断的地方继续往下执行

for n in add():
    print(n)

输出

step 1
1
step 2
3
step 3
5

同样的,把函数改成generator后,基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代

要实验for需要把上面三次调用的代码删除,因为三次调用已经把函数全部执行完了,不会再去执行了。

但是用for循环调用generator时,发现拿不到generator的return语句的返回值

如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

g = fib(6)
while True:
    try:
        x = next(g)
        print('g:',x)
    except StopIteration as e:
        print('Generator return value:',e.value)
        break

generator的工作原理,它是在for循环的过程中不断计算出下一个元素,并在适当的条件结束for循环。对于函数改成的generator来说,遇到return语句或者执行到函数体最后一行语句,就是结束generator的指令,for循环随之结束

注意区分普通函数和generator函数,普通函数调用直接返回结果

generator函数的“调用”实际返回一个generator对象

2.迭代器

凡是可作用于for循环的对象都是Iterable类型;比如list,tuple,dict,set,str,generator等

判断是否为Iterable类型

from collections import Iterable

print(isinstance([], Iterable))

输出

True

凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

直到最后抛出StopIteration错误表示无法继续返回下一个值了。

可以使用isinstance()判断一个对象是否是Iterator对象,生成器都是Iterator对象

from collections import Iterator
print(isinstance((x for x in range(10)), Iterator))

输出

True

集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

'''
学习中遇到问题没人解答?小编创建了一个Python学习交流群:711312441
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
isinstance(iter([]), Iterator)
isinstance(iter('abc'), Iterator)
list=[1,2,3,4]
it = iter(list)
print(next(it))
print(next(it))
#迭代器也可以用常规for语句进行遍历
for x in it:
    print('当前计算:',x)#上面调用了两次,所以这边会接着上一次继续下去,只会打印两句,如果上面只
    #调用一次,这里会调用三次

输出

1
2
当前计算: 3
当前计算: 4

3.总结

凡是可作用于for循环的对象都是Iterable类型 可迭代对象

凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列

集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象

Python的for循环本质上就是通过不断调用next()函数实现的

比较使用list和generator保存斐波拉契数列运算结果的的区别

通过返回 List 能满足复用性的要求,但是当max函数运行占用的内存会随着max的增大而增大

def fab(max):
    n, a, b = 0, 0, 1
    L = []
    while n < max:
        L.append(b)
        a, b = b, a + b
        n = n + 1
    return L
for n in fab(5):
    print(n)

输出

1
1
2
3
5

一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。

yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。

def fab(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b  # 使用 yield
        # print b
        a, b = b, a + b
        n = n + 1
for n in fab(5):
    print(n)

输出

1
1
2
3
5

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Python学习:迭代器与生成器 - Python技术站

(0)
上一篇 2023年4月2日 下午5:02
下一篇 2023年4月2日 下午5:02

相关文章

  • python学习:map函数和filter函数用法教程

    map()函数可以对一个数据进行同等迭代操作。 例如: def f(x): return x * x r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]) print(list(r)) map函数传入的第一个参数就是函数本身,即f。第二个参数是要操作的数据 map() 作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简…

    Python开发 2023年4月2日
    00
  • python中argparse模块的用法

    argparse 模块 一个可执行文件或者脚本都可以接收参数。 $ ls -l /etc /etc 是位置参数 -l 是短选项 如何把这些参数传递给程序呢?从3.2开始Python提供了功能强大的参数分析的模块argparse。 1.参数分类 参数分为: 位置参数,参数放在那里,就要对应一个参数位置。例如/etc就是对应一个参数位置。 选项参数,必须通过前面…

    Python开发 2023年3月31日
    00
  • python练习题:求10万以内的质数

    1、使用质数定义计算 #version1import datetime #导入模块计算效率start = datetime.datetime.now() count = 0 for x in range(2,100000): #求指定范围内的质数 for i in range(2,x): #除以1和本身之外的数 if x % i == 0: break el…

    Python开发 2023年4月2日
    00
  • Python爬虫学习:Cookie 和 Session 的区别是什么?

    一、 含义 Cookie意为“甜饼”,是由W3C组织提出,最早由Netscape社区发展的一种机制。目前Cookie已经成为标准,所有的主流浏览器如IE、Netscape、Firefox、Opera等都支持Cookie。 由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份,怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携…

    Python开发 2023年4月2日
    00
  • Python生成随机数的一个标准库-random

    1.介绍 Random库Python中用于生成随机数的一个标准库。计算机没有办法产生真正的随机数,但它可以产生伪随机数。 伪随机数是计算机按照一定的运算规则产生的一些数据,只不过这些数据表现为随机数的形式。计算机中采用梅森旋转算法生成为随机序列,序列中的每一个元素就是伪随机数,由于计算机不能产生真正的随机数,所以伪随机数也就被称为随机数。 Random库包含…

    python 2023年4月27日
    00
  • Python重用父类方法

    一、重用父类方法 1 与继承没有关系的重用 指名道姓的使用 在子类里想用父类的方法,我们可以直接用父类名.方法名()—>父类里方法有几个参数就传几个参数 我们看起来是子类在调用父类的方法,但是实际上,这并没有存在继承关系 class A: def __init__(self,name,age): self.name=name self.age=ag…

    Python开发 2023年4月2日
    00
  • Python教程:类的继承,什么是继承

    一、什么是继承 继承是一种新建类的方式,新建的类称为子类,被继承的类称为父类 继承的特性是:子类会遗传父类的属性 继承是类与类之间的关系 二、为什么用继承 使用继承可以减少代码的冗余 三、对象的继承 Python中支持一个类同时继承多个父类 class Parent1: pass class Parent2: pass class Sub1(Parent1,…

    Python开发 2023年3月31日
    00
  • 5个非常有意思的python代码,谁运行谁知道

    Python 能做很多无聊,但有意思的事情,例如接下来的一些案例。 Python 整蛊程序 以下程序,不要发代码,要不实现不了你整蛊的目的。 要打包成一个 exe 程序,发给朋友才有意思。 使用 pip install pyinstaller。 打包命令如下: pyinstaller -F 文件名.py 过程中如果出现 BUG(一般是编码错误),点击导航查看…

    Python开发 2023年4月2日
    00
合作推广
合作推广
分享本页
返回顶部