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实现查找匹配项作处理后再替换回去的方法
Jun 10 Python
Python内置函数 next的具体使用方法
Nov 24 Python
django数据库migrate失败的解决方法解析
Feb 08 Python
python实现定时自动备份文件到其他主机的实例代码
Feb 23 Python
基于Python中numpy数组的合并实例讲解
Apr 04 Python
python3+PyQt5+Qt Designer实现堆叠窗口部件
Apr 20 Python
几个适合python初学者的简单小程序,看完受益匪浅!(推荐)
Apr 16 Python
在pyqt5中QLineEdit里面的内容回车发送的实例
Jun 21 Python
python线程定时器Timer实现原理解析
Nov 30 Python
Python实现手机号自动判断男女性别(实例解析)
Dec 22 Python
Python PyPDF2模块安装使用解析
Jan 19 Python
Python基本知识点总结
Apr 07 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
php下获取客户端ip地址的函数
2010/03/15 PHP
php控制linux服务器常用功能 关机 重启 开新站点等
2012/09/05 PHP
php数组相加 array(“a”)+array(“b”)结果还是array(“a”)
2012/09/19 PHP
php导出CSV抽象类实例
2014/09/24 PHP
php实现压缩多个CSS与JS文件的方法
2014/11/11 PHP
PHP基于rabbitmq操作类的生产者和消费者功能示例
2018/06/16 PHP
jquery实现简单的二级导航下拉菜单效果
2015/09/07 Javascript
javaScript实现可缩放的显示区效果代码
2015/10/26 Javascript
node.js回调函数之阻塞调用与非阻塞调用
2015/11/13 Javascript
jquery ztree实现模糊搜索功能
2016/02/25 Javascript
js基于cookie方式记住返回页面用法示例
2016/05/27 Javascript
JS前端笔试题分析
2016/12/19 Javascript
微信小程序实现图片预加载组件
2017/01/18 Javascript
jQuery插件FusionCharts实现的MSBar3D图效果示例【附demo源码】
2017/03/23 jQuery
Bootstrap滚动监听组件scrollspy.js使用方法详解
2017/07/20 Javascript
gulp教程_从入门到项目中快速上手使用方法
2017/09/14 Javascript
解决layer.open弹出框不能获取input框的值为空的问题
2019/09/10 Javascript
Windows和Linux下Python输出彩色文字的方法教程
2017/05/02 Python
解决python线程卡死的问题
2019/02/18 Python
Python实现的登录验证系统完整案例【基于搭建的MVC框架】
2019/04/12 Python
python实现生成Word、docx文件的方法分析
2019/08/30 Python
关于Flask项目无法使用公网IP访问的解决方式
2019/11/19 Python
TensorFlow实现从txt文件读取数据
2020/02/05 Python
Python爬虫逆向分析某云音乐加密参数的实例分析
2020/12/04 Python
CSS3中background-clip和background-origin的区别示例介绍
2014/03/10 HTML / CSS
Fashion Eyewear美国:英国线上设计师眼镜和太阳镜的零售商
2016/08/15 全球购物
丝芙兰巴西官方商城:SEPHORA巴西
2016/10/31 全球购物
Redbubble法国:由独立艺术家设计的独特产品
2019/01/08 全球购物
网站开发实习生的自我评价
2013/12/11 职场文书
二年级小学生评语
2014/04/21 职场文书
小学生学习雷锋倡议书
2014/05/15 职场文书
2014教育局对照检查材料思想汇报
2014/09/23 职场文书
继承公证书格式
2015/01/26 职场文书
2016年小学生寒假家长评语
2015/10/10 职场文书
简短的36句中秋节祝福信息语句
2019/09/09 职场文书
python运算符之与用户交互
2022/04/13 Python