理解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个类
- Node - 这个是1个具有链结构(尾部指针)的数据存储对象
- LinkListIterator - link list 的迭代器, 链表的遍历的核心(根据尾部指针来找到下1个元素)
- 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__ 方法要做到两件事情
- return 当前node 的值(注意是Node 的属性value 而不是Node 对象本身)
- 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 迭代器和可迭代对象
为什么有些对象可以用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 …...
C语言实现动态加载.so动态库,使用,错误捕获以及卸载
动态库 概述 动态库的扩展名是.so。 动态库是被加载,调用的时候是根据内存地址去调用,而不是将代码复制到文件中。 动态库可以同时被多个进程使用。 实战案例:构建 libmath.so 动态库 准备源文件 calc.h 定义加法:int add…...

《动手学深度学习》V2(11-18)
文章目录 十一、二 模型选择与过拟合和欠拟合1、模型的选择2、过拟合和欠拟合3、估计模型容量4、线性分类器的VC维5、过拟合欠拟合的代码实现 :fire:①生成数据集②定义评估损失③定义训练函数④三阶多项式函数拟合⑤线性函数拟合(欠拟合)⑤高阶多项式函数拟合(过拟合) 十三、权…...
web前端之excel转pdf、小黄人发送请求、base64、jspdf、xlsx
MENU 前言方案一方案二结束语 前言 在前端将Excel转换为PDF有多种方案,本文介绍两种简单方案。 方案一 使用jspdf库,先将Excel文件转成Base64格式,然后再使用jspdf库将其转换为PDF格式,最后使用saveAs函数下载PDF文件。 步骤一: 安…...
【面试题】音视频流媒体高级开发(2)
面试题6 衡量图像重建好坏的标准有哪些?怎样计算? 参考答案 SNR(信噪比) PSNR10*log10((2n-1)2/MSE) (MSE是原图像与处理图像之间均方误差,所以计算PSNR需要2幅图像的数据!) SSIM…...

数据与结构--堆
堆 堆的概念 堆:如果有一个关键码的集合K{k0,k1,k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足ki<k2i1且ki<k2i2(或满足ki>k2i1且ki>k2i2),其中i0,1,2,…...

Github的使用教程(下载项目、寻找开源项目和上传项目)
根据『教程』一看就懂!Github基础教程_哔哩哔哩_bilibili 整理。 1.项目下载 1)直接登录到源码链接页或者通过如下图的搜索 通过编程语言对搜索结果进一步筛选。 如何去找开源项目:(Github 新手够用指南 | 全程演示&个人找项目技巧放…...

Linux-线程概念
1. 线程概念 线程:轻量级进程,在进程内部执行,是OS调度的基本单位;进程内部线程共用同一个地址空间,同一个页表,以及内存中的代码和数据,这些资源对于线程来说都是共享的资源 进程:…...
js的桶排序
桶排序(Bucket Sort)是一种分布式排序算法,它将元素分散到一系列桶中,然后对每个桶中的元素进行排序,并将所有的桶合并起来得到最终的排序结果。桶排序适用于输入的元素均匀分布在一个范围内的情况,它的时间…...

解决ubuntu无法上网问题
发现是网络配置成了Manual手动模式,现在都改成自动分配DHCP模式 打开后,尝试上网还是不行,ifconfig查看ip地址还是老地址,怀疑更改没生效,于是重启试试。 重启后,ip地址变了,可以打开网页了 …...
使用nvm管理多版本node.js
使用nvm(Node Version Manager)安装Node.js是一个非常方便的方法,因为它允许你在同一台机器上管理多个Node.js版本。以下是使用nvm安装Node.js的基本步骤: Linux 安装nvm 根据你的操作系统,安装命令可能会有所不同。以…...

推导 模型矩阵的逆转置矩阵求运动物体的法向量
一个物体表面的法向量如何随着物体的坐标变换而改变,取决于变换的类型。使用逆转置矩阵,可以安全地解决该问题,而无须陷入过度复杂的计算中。 法向量变化规律 平移变换不会改变法向量,因为平移不会改变物体的方向。 旋转变换会改…...

定时任务的几种实现方式
定时任务实现的几种方式: 1、JDK自带 (1)Timer:这是java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行。…...

docker部署springboot+Vue项目
项目介绍:后台springboot项目,该项目环境mysql、redis 。前台Vue:使用nginx反向代理 方法一:docker run 手动逐个启动容器 1.docker配置nginx代理 将vue项目打包上传到服务器上。创建文件夹存储数据卷,html存放打包…...

Llama3-Tutorial(Llama 3 超级课堂)-- 笔记
第1节—Llama 3 本地 Web Demo 部署 端口转发 vscode里面设置端口转发 https://a-aide-20240416-b4c2755-160476.intern-ai.org.cn/proxy/8501/ ssh -CNg -L 8501:127.0.0.1:8501 rootssh.intern-ai.org.cn -p 43681参考 https://github.com/SmartFlowAI/Llama3-Tutorial/b…...

【备战软考(嵌入式系统设计师)】12 - 嵌入式系统总线接口
我们嵌入式系统的总线接口可以分为两类,一类是并行接口,另一类是串行接口。 并行通信就是用多个数据线,每条数据线表示一个位来进行传输数据,串行接口就是一根数据线可以来一位一位地传递数据。 从上图也可以看出,并行…...

【一刷《剑指Offer》】面试题 18:树的子结构
力扣对应题目链接:LCR 143. 子结构判断 - 力扣(LeetCode) 牛客对应题目链接:树的子结构_牛客题霸_牛客网 (nowcoder.com) 核心考点:二叉树理解,二叉树遍历。 一、《剑指Offer》对应内容 二、分析问题 二叉…...

17 M-LAG 配置思路
16 华三数据中心最流行的技术 M-LAG-CSDN博客 M-LAG 配置思路 什么是M-LAG?为什么需要M-LAG? - 华为 (huawei.com) 1 配置 M-LAG 的固定的MAC地址 [SW-MLAG]m-lag system-mac 2-2-2 2 配置M-LAG 的系统标识符系统范围1到2 [SW-MLAG]m-lag system-nu…...
深入探索CSS3 appearance 属性:解锁原生控件的定制秘密
CSS3 的 appearance 属性,作为一个强大的工具,让我们得以细致入微地控制元素的外观,特别是对于那些具有平台特定样式的表单元素,如按钮、输入框等。本文不仅会深入解析 appearance 属性的基本工作原理和使用场景,还将通…...
C# 集合(五) —— Dictionary类
总目录 C# 语法总目录 集合五 Dictionary 1. Dictionary 1. Dictionary 字典是键值对集合,通过键值对来查找 Dictionary和Hashtable的区别是Dictionary可以用泛型,而HashTable不能用泛型 OrderedDictionary 是按照添加元素时的顺序的字典,是…...

【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...

2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...