重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
如果函数要返回一系列结果,我们常见的方法就是将结果放到一份列表中,然后返回给调用者。比如下面的函数,返回字符串中每个单词的首字母在真个字符串中的索引:
为定远等地区用户提供了全套网页设计制作服务,及定远网站建设行业解决方案。主营业务为做网站、网站设计、定远网站设计,以传统方式定制建设网站,并提供域名空间备案等一条龙服务,秉承以专业、用心的态度为用户提供真诚的服务。我们深信只要达到每一位用户的要求,就会得到认可,从而选择与我们长期合作。这样,我们也可以走得更远!
运行结果:
上述的结果完全符合我们的预期,但 get_word_index 函数不够简洁。下面我们尝试使用生成器来实现:
运行结果:
改写之后,不仅运行结果符合要求,由于不需要和 result 列表交互,函数也变得非常简洁。下面我们就来详细学习下生成器吧~
生成器是指使用 yield 表达式的函数,调用生成器函数时,它并不会真的运行,而是会返回迭代器。每次在这个迭代器上面调用内置的 next 函数时,迭代器就会把生成器推进到下一个 yield 表达式那里。生成器传给 yield 的值均会由迭代器返回给调用者。
此外,如果输入量非常大,使用列表作为返回值,那么程序就有可能耗尽内存并崩溃。相反,使用生成器之后,则可以应对任意长度的输入数据。
例如,下面这个生成器函数可以获取文件中单词的索引,而不管文件内容多大,该函数执行时消耗的内存,只由单行的文本长度决定:
其中 test_generator.txt 中的内容如下:
运行结果:
下面这句话特别重要: 生成器函数返回的迭代器,是由状态的,及调用者不应该反复使用它 。我们那 word_index_iter 来说明:
如果想重复调用,请将其封装成容器:
运行结果:
关于上述自定义容器的实现原理,我的另外一篇文章做了详细介绍,链接奉上:
Python 中的 yield 关键字鲜为人知,但是作用却很大,正是因为有了yield,才有了Python生成器。
yield 是 Python 的关键字,它用于 从函数返回而不破坏其局部变量的状态 ,并且在调用该函数时,从最后一个 yield 语句开始执行。任何包含 yield 关键字的函数都称为生成器。
Python 中的 yield 关键字的作用类似于 Python 中的 return 语句,不同之处在于:
yield的优点
yield的缺点
Python 可以使用 括号() 创建生成器
更多时候,我们使用 yield 关键字创建生成器
下面这个生成器,前4次调用它时,返回的是0-3这几个特殊值,第5次调用它时返回一个10-20之间的随机整数。
更多时候,生成器可以返回无限的值。
注意 generator() 函数返回的是一个生成器对象,要想获取它的值,可以像上面那样在迭代器中取出它的值,我们也可以显式的调用next函数获取值。
Python | yield Keyword - GeeksforGeeks:
生成器似乎并不是一个经常被开发者讨论的语法,因此也就没有它的大兄弟迭代器那么著名。大家不讨论它并不是说大家都已经对它熟悉到人尽皆知,与之相反,即使是工作多年的开发者可能对生成器的运行过程还是知之甚少。这是什么原因导致的呢?我猜想大概有以下几点原因: (1)运行流程不同寻常,(2)日常开发不需要,(3)常常将生成器与迭代器混淆。 生成器的运行流程可以按照协程来理解,也就是说 返回中间结果,断点继续运行 。这与我们通常对于程序调用的理解稍有差异。这种运行模式是针对什么样的需求呢? 一般而言,生成器是应用于大量磁盘资源的处理。 比如一个很大的文件,每次读取一行,下一次读取需要以上一次读取的位置为基础。下面就通过代码演示具体看看生成器的运行机制、使用方式以及与迭代器的比较。
什么是生成器?直接用文字描述可能太过抽象,倒不如先运行一段代码,分析这段代码的运行流程,然后总结出自己对生成器的理解。
从以上演示可以看出,这段代码定义了一个函数,这个函数除了yield这个关键字之外与一般函数并没有差异,也就是说生成器的魔法都是这个yield关键字引起的。 第一点,函数的返回值是一个生成器对象。 上述代码中,直接调用这个看似普通的函数,然后将返回值打印出来,发现返回值是一个对象,而并不是普通函数的返回值。 第二点,可以使用next对这个生成器对象进行操作 。生成器对象天然的可以被next函数调用,然后返回在yield关键字后面的内容。 第三,再次调用next函数处理生成器对象,发现是从上次yield语句之后继续运行,直到下一个yield语句返回。
生成器的运行流程确实诡异,下面还要演示一个生成器可以执行的更加诡异的操作:运行过程中向函数传参。
返回生成器和next函数操作生成器已经并不奇怪了,但是在函数运行过程中向其传参还是让人惊呆了。 调用生成器的send函数传入参数,在函数内使用yield语句的返回值接收,然后继续运行直到下一个yield语句返回。 以前实现这种运行流程的方式是在函数中加上一个从控制台获取数据的指令,或者提前将参数传入,但是现在不用了,send方式使得传入的参数可以随着读取到的参数变化而变化。
很多的开发者比较容易混淆生成器和迭代器,而迭代器的运行过程更加符合一般的程序调用运行流程,因此从亲进度和使用熟悉度而言,大家对迭代器更有好感。比如下面演示一个对迭代器使用next方法进行操作。
从以上演示来看,大家或许会认为迭代器比生成器简单易用得太多了。不过,如果你了解迭代器的实现机制,可能就不会这么早下结论了。python内置了一些已经实现了的迭代器使用确实方便,但是如果需要自己去写一个迭代器呢?下面这段代码就带大家见识以下迭代器的实现。
在python中,能被next函数操作的对象一定带有__next__函数的实现,而能够被迭代的对象有必须实现__iter__函数。看了这么一段操作,相信大家对迭代器实现的繁琐也是深有体会了,那么生成器的实现是不是会让你觉得更加简单易用呢?不过千万别产生一个误区,即生成器比迭代器简单就多用生成器。 在实际开发中,如果遇到与大量磁盘文件或者数据库操作相关的倒是可以使用生成器。但是在其他的任务中使用生成器难免有炫技,并且使逻辑不清晰而导致可读性下降的嫌疑。 这大概也能解释生成器受冷落的原因。不过作为一个专业的开发者,熟悉语言特性是分内之事。
到此,关于生成器的讨论就结束了。本文的notebook版本文件在github上的cnbluegeek/notebook仓库中共享,欢迎感兴趣的朋友前往下载。
迭代器
迭代是Python最强大的功能之一,是访问集合元素的一种方式。
迭代器是一个可以记住遍历的位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束,迭代器只能往前不会后退。
迭代器有两个基本的方法:iter()和next()。
生成器
在Python中,使用了yield的函数被称为生成器。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
在调用生成器运行的过程中,每次遇到yield时函数会暂停并保存当前所有的运行信息,返回yield的值,并在下一次执行next()方法时从当前位置继续运行。
调用一个生成器函数,返回的是一个迭代器对象。
迭代器与生成器之间的区别:
迭代器是一个更抽象的概念,任何对象,如果它的类有NEXTiter方法返回自己本身,对于string、list、dict、tuple等这类容器对象,使用for循环遍历是很方便的。在后台For语言对容器对象条用iter()函数,iter()是Python的内置函数。iter()会返回一个定义了next()方法迭代器对象,在容器中逐个访问容器的元素,next()也是Python的内置函数,next()会抛出StopIteration异常。
生成器是创新迭代器的简单而强大的工具,它们写起来就好像正则函数,只是在需要返回数据的时候使用yield 语句。
迭代器协议,对象需要提供next()方法,它要么返回迭代中的下一项,要么就引起一个StopIteration异常,终止迭代。
可迭代对象,实现了迭代器协议对象。list、tuple、dict都是Iterable可迭代的对象,但不是Iterator迭代器对象。