理解python中的Iterator 和 Iterable 迭代器和可迭代对象

2024-05-12 08:04

本文主要是介绍理解python中的Iterator 和 Iterable 迭代器和可迭代对象,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

为什么有些对象可以用for … in 循环

我们先看一段代码:

    list = [1, 2, 3, 4, 5]for i in list:logger.info(i)

这代码定义了1个数组object list, 然后用 for … in 来遍历这个list
看起来合理没什么值得注意

但其实 for … in 后面对象还可以是个String

for i in "hello":logger.info(i)

还可以是个dict (相当于java 的map)

    dict = {"a": 1, "b": 2}for key in dict:logger.info("{}:{}".format(key, dict[key]))

甚至可以是1个文件 open 对象 (TextIOWrapper)

    with open("/home/gateman/Documents/jumpserver_installation.log") as f:for i in f:logger.info(i)

这样就不是那么能理解了,
for i in loopable_object 这种写法, 用java 的思维来推导 这个loopable_object 一定是实现了某个接口 类似与Loopable

实际上, 这个想法不是全错, 虽然python 没有强接口概念, 但是实际上, python 这个Loopable_object 必须具有 __iter__方法
也就是讲 它必须是1个iterable 对象




iterable 和 iterator 的定义

iterable :

Iterable 的中文意思是可迭代对象, 就是可以被循环的对象, 它在内部必须实现__iter__ 方法
iter 方法会返回1个 iterator , 所以实际上iterable 是依赖于它内置的 iterator去迭代元素的

常见的iterable 有List, Dict, String 等等, 所以它们都是可以用for … in … 来循环的




iterator:

上面说了, iterator 中文是迭代器, 它才是真正可以被迭代的对象, 它必须实现__next__ 方法

可以理解为 Iterator 有1个属性 current-item
next 方法会return 当前的current-item
而且会把 current -item 指向 下个item (置于如何找到下个item case by case, 看具体实现)

所以下次调用__next__ 就会返回上一次调用的next item了

而且 iterator 对象也可以用 python built-in 函数next() 来调用

例如

	def test_iterator():list = [1, 2, 3]iter = list.__iter__()while True:try:print(next(iter))except StopIteration:break

上面代码我们多次调用next(iter) 也能实现遍历, 当尝试去获取最后1个元素的next() 对象时,会产生StopIteration Exception





简单归纳下:

真正 可以遍历的东西是 iterator 迭代器, 它必须实现__next__ 接口用于 返回当前的item并改变状态令当前item指向下个

而 iterable 可迭代对象 里面必须实现__iter__ 方法来内置1个iterator
这样iterable 可以被 for … in 来遍历

实际上, 大部分iterator对象, 除了实现__next__方法外, 还实现__Iter__ 方法但让其return 自身

    def __iter__(self):return self

这样 这个iterator 本身也是个iterable 。
而且官方貌似鼓励这么做, 但是个人不是很喜欢。




写1个自定义 iterator 和iterable 例子, python 中的链表

python 不像java 没有内置链表 link list 这个容器.

但是我们可以利用迭代器自己写1个, 当然只实现链表部分简单的功能。

实现这个链表需要3个类

  1. Node - 这个是1个具有链结构(尾部指针)的数据存储对象
  2. LinkListIterator - link list 的迭代器, 链表的遍历的核心(根据尾部指针来找到下1个元素)
  3. LinkList - Iterable , 它的__iter__ 会返回上面的iterator, 但是它还包括链表的一些操作, 例如构建链表, append 等




Node 类

很简单定义1个value 属性和 next 属性就行

class Node:def __init__(self, value):self._value = valueself._next = None@propertydef value(self):return self._value@propertydef next(self):return self._next@next.setterdef next(self, next):self._next = next@value.setterdef value(self, value):self._value = value

测试代码:

def test_node():logger.info("test_node")node = Node(1)assert node.value == 1node.value = 2assert node.value == 2def test_node_next():logger.info("test_node_next")node1 = Node(1)node2 = Node(2)node1.next = node2assert node1.next == node2assert node1.next.value == 2

置于这里的value 可以传入任意类的对象, 天然泛型了这是




LinkListIterator 类定义

上提到了, LinkListIterator
必须有1个current_node 对象保存当前的Node 是什么
而的__next__ 方法要做到两件事情

  1. return 当前node 的值(注意是Node 的属性value 而不是Node 对象本身)
  2. current_node 要指向下1个item

如果当前node 已经是找不到or None, 则raise StopIteration
为了也能用for … in 来循环它, 我还是让它也实现__iter__ return 其本身
所以这里LinkListIterator 实际是也是iterable(it 上而不是业务逻辑上)

class LinkListIterator:def __init__(self, _first_node) -> None:self._current_node = _first_nodedef __iter__(self):return selfdef __next__(self):if not self._current_node:raise StopIterationcurrent = self._current_nodeself._current_node = current.nextreturn current.value




LinkListIterator 类测试

我们写1个元素类作为测试
staff.py

from loguru import logger
class Staff:def __init__(self, id, name):self._id = idself._name = namedef __repr__(self):return "Staff({}, {})".format(self._id, self._name)

就两个属性id 和 name

测试代码:

def sample2():bill = Staff(2, "bill")jack = Staff(1, "jack")mike = Staff(3, "mike")bill_node = Node(bill)bill_node.next = Node(jack)bill_node.next.next = Node(mike)linkListIterator = LinkListIterator(bill_node)logger.info(linkListIterator._current_node.value) # bill# next() is a built-in function that will call the __next__() method of the iterator# in this case , it will return node's value but not node itselflogger.info(next(linkListIterator)) # jacklogger.info(next(linkListIterator)) # mikelogger.info(linkListIterator._current_node.value) # mike# if we want use for loop, we need to implement __iter__() method in LinkListIterator# otherwise we will get TypeError: 'LinkListIterator' object is not iterablefor staff in linkListIterator: logger.info(staff)

其实看出, linkListIterator 可以作为1个链表容器, 遍历对象, 但是并不优雅, 它向使用者暴露了Node 这个中间数据仓库类.




LinkList 类定义

为了真正的实现链表功能, 我们还需要1个容器类LinkList, 而它必须是1个iterator, 它只要让 iter 指向LinkListIterator就好

from loguru import loggerfrom src.iterator.sample_link_list.link_list_iterator import LinkListIterator
from src.iterator.sample_link_list.node import Nodeclass LinkList:def __init__(self, first) -> None:node = Node(first)_first_node = node_last_node = nodedef __init__(self, *values) -> None:if len(values) < 1:raise ValueError("At least one node is required")self._first_node = Node(values[0])current = self._first_nodefor i in range(1, len(values)):current.next = Node(values[i])current = current.nextself._last_node = currentdef __iter__(self):return LinkListIterator(self._first_node)# to print all nodes's value but not nodes themselvesdef print_nodes(self):current = self._first_nodewhile current:logger.info(current.value)current = current.nextdef get_length(self):current = self._first_nodecount = 0while current:count += 1current = current.nextreturn countdef append(self, value):if self.get_length() == 0:self._first_node = Node(value)self._last_node = self._first_nodeelse:self._last_node.next = Node(value)self._last_node = self._last_node.next

这个LinkList 实现了两种构造方法, 接受单个元素和一组元素作为参数
而且它在内部调用Node 对象, 向用户隐藏了这个细节

它有两个关键内部属性, 头指针 和 尾部指针
其实理论上有头指针就可以, 但是留有尾部指针可以大大 减少 append 元素方法的内部查询次数

我在这个类 只实现了 get_length() append() print_nodes() (其实可以用for loop 代替) 方法

有必要的话 删除元素, 中间插入元素, 检查元素是否存在的方法还是可以再加上的




LinkList 类测试

测试代码:


def sample3():bill = Staff(2, "bill")jack = Staff(1, "jack")mike = Staff(3, "mike")link_list = LinkList(jack)link_list.print_nodes()link_list = LinkList(bill, jack, mike);link_list.print_nodes()logger.info("length of link_list: {}".format(link_list.get_length())) # 3link_list.append(Staff(4, "Ted"))link_list.append(Staff(5, "Peter"))logger.info("length of link_list: {}".format(link_list.get_length())) # 5for i in link_list:logger.info(i)

可以见到, 使用iterable 比直接用iterator 优雅得多了, 符合人类正常的思维。

这篇关于理解python中的Iterator 和 Iterable 迭代器和可迭代对象的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/982057

相关文章

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

nudepy,一个有趣的 Python 库!

更多资料获取 📚 个人网站:ipengtao.com 大家好,今天为大家分享一个有趣的 Python 库 - nudepy。 Github地址:https://github.com/hhatto/nude.py 在图像处理和计算机视觉应用中,检测图像中的不适当内容(例如裸露图像)是一个重要的任务。nudepy 是一个基于 Python 的库,专门用于检测图像中的不适当内容。该

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

pip-tools:打造可重复、可控的 Python 开发环境,解决依赖关系,让代码更稳定

在 Python 开发中,管理依赖关系是一项繁琐且容易出错的任务。手动更新依赖版本、处理冲突、确保一致性等等,都可能让开发者感到头疼。而 pip-tools 为开发者提供了一套稳定可靠的解决方案。 什么是 pip-tools? pip-tools 是一组命令行工具,旨在简化 Python 依赖关系的管理,确保项目环境的稳定性和可重复性。它主要包含两个核心工具:pip-compile 和 pip

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念