作者回复: yield的主要用途是需要一个数据时,才产生一个,而不是把数据线一次性存入内存;相对于把数据提前定义成列表来使用,要极为节省系统资源。 一般访问生成器要使用next方法,也可以使用list方法一次性讲将所有值读取出来,但是一次性读取出来就和列表一样了,失去自身的优势。
作者回复: 通常一个函数返回执行结果使用的是return关键字,但是在实际使用中发现如果函数返回的内容非常多而且是按顺序每次只使用一个元素的话,我们需要把结果再处理成可迭代对象,例如返回一个列表,我们要使用for..in...的形式再对返回结果做处理,这时候可以使用yield关键字来返回,有效的避免了一次返回数据过多,占用较多内存的问题。这就是为什么有了return还要产生yield的原因了。这里还有一些注意事项,使用yield关键字的函数叫做生成器,他返回的结果是可迭代对象,只能读取一次。更多的原理请参考stackoverflow : https://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do
作者回复: 出现这样计算结果的原因是浮点数缺乏精确性导致的,如在python中计算 1+0.2+0.2+0.2, 发现执行结果和你的经验不一样了吗? 不止在python语言中存在这样的问题,凡是实数计算都会存在无限的精度跟有限的内存之间的矛盾 在系统底层计算浮点数时是使用二进制经过了它的转换,就出现了你看到的“误差” 要想精确计算可以使用Decimal库,就可以避免这种情况了 from decimal import Decimal a = Decimal('1') b = Decimal('0.2') print(a+b+b+b)
作者回复: 这里看结果很相似,但是其底层实现是不一样的, for in 使用的是 __iter__ 魔术方法 next 使用的是 __next__ 魔术方法 我们把能for in 的对象称作可迭代Iterable 我们把又能for in 又能next 的对象称作迭代器 Iterator
作者回复: 带yield的函数我们称为迭代器,这种函数返回的是一个固定的对象,叫迭代器对象,它和return的最大区别是,如果你需要返回无限序列,return会产生一个巨大的列表,很明显存在内存限制问题。所以引入了yield返回一个固定长度的值。 确实如你所说,python虚拟机有一个调用栈,以python3.7为例,在Python-3.7.0\Include\frameobject.h 17行定义了PyFrameObject结构体,用来保存最后执行的指令、异常和命名空间。 当新创建一个迭代器时会调用Python-3.7.0\Objects\genobject.c 817行 PyGen_New(PyFrameObject *f) 函数。 我们知道调用yield其实就是调用迭代器的next方法,也就是调用了gen_iternext(PyGenObject *gen)函数,最终调用的是152行的gen_send_ex(PyGenObject *gen, PyObject *arg, int exc) 函数。 这里的Py_INCREF(result)函数用于记录引用计数器并加一, *(f->f_stacktop++) = result 209行这里是参数压栈操作了。
作者回复: 是的,学习就是一种从不懂到懂,从陌生到熟悉的过程,但是要想透彻的掌握一门语言还要多了解它背后的原理和技术,继续努力吧
作者回复: 是可以捕获的,只是因为stopiteration异常没有额外的返回信息,导致你输出的e没有任何内容,让你误以为捕获失败,将e换成其他提示信息可以看到捕获正常
作者回复: 需要看到你的完整代码和报错信息,如果是按照演示的程序修改,会报错为frange是非迭代对象,也就是没有yield没有迭代功能,不能使用for in的语法调用
作者回复: 多去尝试,yield还有扩展的用法,在掌握了基础用法之后可以通过官方文档加深对yield的理解
作者回复: 通过type()查看frange会发现,当你调用只会frange会变成<class 'generator'>类型,而生成器类型是不会自动执行的,必须用next() 或者list()取其中的元素,和你想的会按顺序执行不太一样