Python通过for循环理解迭代器和生成器实例详解


Posted in Python onFebruary 16, 2019

本文实例讲述了Python通过for循环理解迭代器和生成器。分享给大家供大家参考,具体如下:

迭代器

可迭代对象

通过 for…in… 循环依次拿到数据进行使用的过程称为遍历,也叫迭代。我们把可以通过 for…in… 语句迭代读取数据的对象称之为可迭代对象。

- 通过 isinstance()可以判断一个对象是否可以迭代

# 判断列表
print(isinstance([], Iterable)

打印结果为 True 即为可迭代对象。

- 自定义一个能容纳数据的类,测试该类的可迭代性

import collections
class MyClassmate(object):
  def __init__(self):
    self.names = []
  def add(self, name):
    self.names.append(name)
# 创建 MyClassmate对象
my_classmate = MyClassmate()
my_classmate.add("小王")
my_classmate.add("小李")
my_classmate.add("小张")
# 判断MyClassmate是否为可迭代对象
print("是否为可迭代对象:",isinstance(my_classmate, collections.Iterable))
# 迭代数据
for temp in my_classmate:
  print(temp)

运行结果:

是否为可迭代对象: False
Traceback (most recent call last):
    for temp in my_classmate:
TypeError: 'MyClassmate' object is not iterable

封装一个可以存放多条数据的类型是不可迭代的

何为可迭代对象

  • 我们分析对可迭代对象进行迭代使用的过程,发现每迭代一次(即在 for…in… 中每循环一次)都会返回对象中的下一条数据,一直向后读取数据直到迭代了所有数据后结束。那么,在这个过程中就应该有一个"人"去记录每次访问到了第几条数据,以便每次迭代都可以返回下一条数据。我们把这个能帮助我们进行数据迭代的"人"称为迭代器 (Iterator)。
  • 可迭代对象的本质就是提供一个这样的中间"人"即迭代器帮助我们对其进行迭代遍历使用。
  • 可迭代对象通过__iter__方法向我们提供一个迭代器,在迭代一个可迭代对象的时候,实际上就是先获取该对象提供的一个迭代器,然后通过这个迭代器来依次获取对象中的每一个数据.

1.可迭代对象的本质就是提供一个这样的中间"人"即迭代器帮助我们对其进行迭代遍历使用

2.可迭代对象是一个具备了__iter__方法的对象,通过__iter__方法获取可迭代对象的迭代器

from collections import Iterable
class MyClassmate(object):
  def __init__(self):
    self.names = []
  def add(self, name):
    self.names.append(item)
  def __iter__(self):
    """空实现该方法"""
    return None
# 创建 MyClassmate对象
my_classmate = MyClassmate()
my_classmate.add("小王")
my_classmate.add("小李")
my_classmate.add("小张")
# 判断MyClassmate是否为可迭代对象
print(isinstance(my_classmate, Iterable))

运行结果:

是否为可迭代对象: True

这回测试发现添加了__iter__方法的my_classmate对象已经是一个可迭代对象了。

iter() 函数与 next() 函数

list、tuple 等都是可迭代对象,我们可以通过 iter() 函数获取这些可迭代对象的迭代器。然后我们可以对获取到的迭代器不断使用 next() 函数来获取下一条数据。

li = [11, 22, 33, 44, 55]
# 通过iter() 取得可迭代对象的迭代器
iterator = iter(li)
# 通过next()函数取得iterator迭代器指向的下一个值
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))

1.iter(iterable) 函数是把可迭代对象的迭代器取出来,内部是调用可迭代对象的__iter__方法,来取得迭代器的

2.next(iterator) 函数是通过迭代器取得下一个位置的值,内部是调用迭代器对象的__next__方法,来取得下一个位置的值

注意: 当我们已经迭代完最后一个数据之后,再次调用 next() 函数会抛出 StopIteration 的异常,来告诉我们所有数据都已迭代完成,不用再执行 next() 函数了。

迭代器

我们要想构造一个迭代器,就要实现它的__next__ 方法。但这还不够,python 要求迭代器本身也是可迭代的,所以我们还要为迭代器实现__iter__ 方法,而 __iter__ 方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的 __iter__ 方法返回自身即可。

一个实现了 __iter__ 方法和 __next__ 方法的对象,就是迭代器,迭代器同时也是一个可迭代对象.

import collections
class MyClassmate(object):
  def __init__(self):
    # 声明一个列表
    self.names = []
    # 记录迭代器迭代的位置, 默认是0 ,即从起始位置开始
    self.current = 0
  def add(self, name):
    self.names.append(name)
  def __iter__(self):
    """通过该方法取得迭代器对象"""
    return self
  def __next__(self):
    """取得下一个迭代的值"""
    if self.current < len(self.names):
      name = self.names[self.current]
      self.current += 1
      return name
    else:
      raise StopIteration
# 创建MyClassmate实例
my_classmate = MyClassmate()
my_classmate.add("小王")
my_classmate.add("小李")
my_classmate.add("小张")
# 测试MyList是不是可迭代对象
print(isinstance(my_classmate, collections.Iterable))
# 遍历数据
for name in my_classmate:
  print(name)

for…in… 循环的本质

for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象 Iterable 的迭代器,然后对获取到的迭代器不断调用 next() 方法来获取下一个值并将其赋值给 item,当遇到 StopIteration 的异常后循环结束 (for…in..会自动处理 StopIteration 异常)。

生成器

生成器

生成器是一种特殊的迭代器,它比迭代器更优雅

创建生成器的方法

1.将列表生成式的 [] 改成 ()

# 参考列表生成式
L=[x*2 for x in range(6)]
print(L)
# 把[] 改为() :就是一个简单的列表生成器
G=(x*2 for x in range(6))
# 输出的是生成器对象
print(G)
print("通过next()函数取得下一个值")
print(next(G))
print(next(G))
print(next(G))
print(next(G))
print(next(G))
print(next(G))
# 创建一个简单生成器,通过 for来遍历
G=(x*2 for x in range(6))
print("通过for 迭代的结果:")
for num in G:
  print(num)

运行结果:

[0, 2, 4, 6, 8, 10]
<generator object <genexpr> at 0x7ff7f8bbd5c8>
通过next()函数取得下一个值
0
2
4
6
8
10
通过for 迭代的结果:
0
2
4
6
8
10

2.通过关键字 yield 实现生成器

def fib(n):
  current_index = 0
  num1, num2 = 0, 1
  while current_index < n:
    # print(num1) # 打印斐波那契数列
    """
     1. 假如函数中有yield,则不再是函数,而是生成器
     2. yield 会产生一个断点
     3. 假如yield后面紧接着一个数据,就会把数据返回,
      作为next()函数或者for ...in...迭代出的下一个值
    """
    yield num1
    num1, num2 = num2, num1 + num2
    current_index += 1
if __name__ == '__main__':
  # 假如函数中有yield,则不再是函数,而是一个生成器
  gen = fib(5)
  #  生成器是一种特殊的迭代器
  for num in gen:
    print(num)
  # 也可以用 next() 函数取下一个值

在使用生成器实现的方式中,我们将原本在迭代器__next__方法中实现的基本逻辑放到一个函数中来实现,但是将打印输出方式换成 yield,此时新定义的函数便不再是函数,而是一个生成器了。简单来说:只要在函数中有 yield 关键字,就称为生成器。

此时按照调用函数的方式( 案例中为 gen = fib(5) )使用生成器就不再是执行函数体了,而是会返回一个生成器对象( 案例中为 gen ),然后就可以按照使用迭代器的方式来使用生成器了。

使用 send() 唤醒

def gen():
  i = 0
  while i < 5:
    temp = yield i
    print(temp)
    i += 1
if __name__ == '__main__':
  # 取得生成器对象
  obj = gen()
  # 使用next()唤醒生成器
  print(next(obj))
  print(next(obj))
  # 使用send唤醒生成器 ,在唤醒的同时向断点处传入一个附加数据
  print(obj.send("haha"))
  # 使用next()唤醒生成器
  print(next(obj))
  # 使用send唤醒生成器 ,在唤醒的同时向断点处传入一个附加数据
  print(obj.send("python"))

运行结果:

0
None
1
haha
2
None
3
python

我们除了可以使用 next() 函数来唤醒生成器继续执行外,还可以使用 send() 函数来唤醒执行。使用 send() 函数的一个好处是可以在唤醒的同时向断点处传入一个附加数据。

总结

1. 假如函数中有 yield,则不再是函数,而是生成器
2. yield 会产生一个断点,暂停函数,保存状态
3. 假如yield后面紧接着一个数据,就会把数据返回,作为 next() 函数或者 for …in… 迭代出的下一个值
4. 可以通过 next() 唤醒生成器,让生成器从断点处继续执行

send与next唤醒生成器不同:

1. send 与next都可以唤醒生成器,但send(value)可以传值给生成器的断点处
2. 使用:

next(generator)
generator.send("你好")

3. generator.send(None)等价于next(generator)
4. 注意: 第一次唤醒生成器时,假如使用 send,则只能传 None,因为刚开始执行生成器时,是没有断点的

- 解析

temp = yield num
generator.send("你好")

temp = yield num 为赋值语句,当看到等号时, 一定是等号左边先运行完,再赋值给等号右边

而程序运行到 yield num 时,会先返回一个值,也就是此时的 num ,然后将 send()里的参数传给 yield num,进而赋值给 temp

更多关于Python相关内容可查看本站专题:《Python数据结构与算法教程》、《Python Socket编程技巧总结》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》及《Python入门与进阶经典教程》

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
Python中使用HTMLParser解析html实例
Feb 08 Python
在Python中实现贪婪排名算法的教程
Apr 17 Python
django实现分页的方法
May 26 Python
Python函数中的函数(闭包)用法实例
Mar 15 Python
python中实现延时回调普通函数示例代码
Sep 08 Python
tensorflow1.0学习之模型的保存与恢复(Saver)
Apr 23 Python
python实现两个一维列表合并成一个二维列表
Dec 02 Python
SpringBoot实现登录注册常见问题解决方案
Mar 04 Python
Python使用Paramiko控制liunx第三方库
May 20 Python
pycharm不以pytest方式运行,想要切换回普通模式运行的操作
Sep 01 Python
Python多分支if语句的使用
Sep 03 Python
PYTHON基于Pyecharts绘制常见的直角坐标系图表
Apr 28 Python
Python3 导入上级目录中的模块实例
Feb 16 #Python
对Python3 goto 语句的使用方法详解
Feb 16 #Python
解决Pycharm调用Turtle时 窗口一闪而过的问题
Feb 16 #Python
Python实现定时自动关闭的tkinter窗口方法
Feb 16 #Python
对IPython交互模式下的退出方法详解
Feb 16 #Python
python交互界面的退出方法
Feb 16 #Python
详解重置Django migration的常见方式
Feb 15 #Python
You might like
JAVA/JSP学习系列之六
2006/10/09 PHP
php实现RSA加密类实例
2015/03/26 PHP
浅谈PHP链表数据结构(单链表)
2016/06/08 PHP
Ajax+PHP实现的删除数据功能示例
2019/02/12 PHP
PHP基于swoole多进程操作示例
2019/08/12 PHP
laravel5.0在linux下解决.htaccess无效和去除index.php的问题
2019/10/16 PHP
疯掉了,尽然有js写的操作系统
2007/04/23 Javascript
10个实用的脚本代码工具
2010/05/04 Javascript
用jQuery中的ajax分页实现代码
2011/09/20 Javascript
获取服务器传来的数据 用JS去空格的正则表达式
2012/03/26 Javascript
Bootstrap每天必学之弹出框(Popover)插件
2016/04/25 Javascript
探讨跨域请求资源的几种方式(总结)
2016/12/02 Javascript
Vue 单文件中的数据传递示例
2017/03/21 Javascript
在React中如何优雅的处理事件响应详解
2017/07/24 Javascript
微信小程序之数据缓存的实例详解
2017/09/29 Javascript
NodeJS安装图文教程
2018/04/19 NodeJs
详解element-ui中form验证杂记
2019/03/04 Javascript
JQuery实现简单的复选框树形结构图示例【附源码下载】
2019/07/16 jQuery
Vue 利用指令实现禁止反复发送请求的两种方法
2019/09/15 Javascript
JS+CSS实现过渡特效
2021/01/02 Javascript
Python 专题三 字符串的基础知识
2017/03/19 Python
Python实现可自定义大小的截屏功能
2018/01/20 Python
Python3中lambda表达式与函数式编程讲解
2019/01/14 Python
Python hexstring-list-str之间的转换方法
2019/06/12 Python
django 扩展user用户字段inlines方式
2020/03/30 Python
Python分析微信好友性别比例和省份城市分布比例的方法示例【基于itchat模块】
2020/05/29 Python
详解如何使用CSS3中的结构伪类选择器和伪元素选择器
2020/01/06 HTML / CSS
HTML5 canvas基本绘图之绘制线段
2016/06/27 HTML / CSS
G-Form护具官方网站:美国运动保护装备
2019/09/04 全球购物
Theflamel意大利:女士奢华服装、鞋子和配件
2020/01/11 全球购物
农药学硕士毕业生自荐信
2013/09/25 职场文书
2014年学习全国道德模范事迹思想汇报
2014/09/15 职场文书
接待员岗位职责范本
2015/04/15 职场文书
就业指导讲座心得体会
2016/01/15 职场文书
《当代神农氏》教学反思
2016/02/23 职场文书
MySQL Innodb关键特性之插入缓冲(insert buffer)
2021/04/08 MySQL