python高级(1): 迭代器详解

文章目录

    • 1. 迭代器与可迭代对象(Iterable)
      • 1.1 可迭代对象(Iterable)
      • 1.2 迭代器( Iterator)
    • 2. 自定义一个可迭代器
      • 2.1 实现迭代器
      • 2.2 for 遍历迭代器的过程
    • 3. yolov8 Dataset实现案例

Python迭代器的作用是提供一种遍历数据集合的方式。它是一个可以被迭代的对象,可以使用迭代器的方法来逐个访问集合中的元素,而不需要事先知道集合的大小。

在深度学习的DatasetDataloader中,就是通过迭代器实现的,因此迭代器是一个非常重要的概念和工具

迭代器具有以下几个重要的特点:

  • 节省内存:迭代器一次只返回一个元素,不需要一次性将整个集合加载到内存中,这样可以节省内存空间,特别是在处理大型数据集合时非常有用。
  • 惰性计算:迭代器在需要时才会计算下一个元素,而不是一次性计算所有的元素。这种惰性计算的方式可以在处理大量数据时提高效率。
  • 可逆迭代:迭代器可以反向遍历集合,而不需要额外的复制和存储。
  • 支持并行处理:迭代器可以同时遍历多个集合,实现并行处理。

总之,迭代器提供了一种灵活、高效和节省内存的方式来遍历数据集合,是Python中非常重要的概念和工具。

1. 迭代器与可迭代对象(Iterable)

1.1 可迭代对象(Iterable)

表示该对象可迭代, 它的类中需要定义__iter__方法,只要是实现了__iter__方法的类就是可迭代对象

from collections.abc import Iterable, Iterator

class A(object):
   def __init__(self):
       self.a = [1, 2, 3]

   def __iter__(self):
       # 此处返回啥无所谓
       return self.a

cls_a = A()
#  True
print(isinstance(cls_a, Iterable))
  • 可迭代对象,必须具备__iter__这个特殊函数,并且返回一个可迭代对象。可以通过isinstance(cls_a, Iterable)可以判断是否是可迭代对象

  • 如果一个Iterable,仅仅定义了__iter__方法,是没有特别大的用途,因为依然无法迭代,实际上 Iterable 仅仅是提供了一种抽象规范接口

1.2 迭代器( Iterator)

  • 迭代器Iterator一定是可迭代对象Iterable,但反过来,可迭代对象不一定是迭代器,因为迭代器只是可迭代对象的一种表示形式。
  • 实现了__next____iter__方法的类才能称为迭代器 就可以被 for 循环遍历数据。

因此,通过自定义实现迭代器Iterator,必须具备__next____iter__ 两个方法,如下案例所示:

class A(object):
    def __init__(self):
        self.index = -1
        self.a = [1, 2, 3]

    # 必须要返回一个实现了 __next__ 方法的对象,否则后面无法 for 遍历
    # 因为本类自身实现了 __next__,所以通常都是返回 self 对象即可
    def __iter__(self):
        return self

    def __next__(self):
        self.index += 1
        if self.index < len(self.a):
            return self.a[self.index]
        else:
            # 抛异常,for 内部会自动捕获,表示迭代完成
            raise StopIteration("遍历完了")

cls_a = A()
print(isinstance(cls_a, Iterable)) # True 
print(isinstance(cls_a, Iterator)) # True  从这里可以看出来同时具有__iter__和__next__的类,是Iterator
print(isinstance(iter(cls_a), Iterator)) # True 这里加不加iter()都一样,因为这个类里面的iter也是直接返回自身(self)

#另外补充一点这个a和上面类里面的a是不一样的;这里的用i(任意字母都可以)也能
for a in cls_a:
    print(a)
# 打印 1 2 3

-可以看到,通过实现__iter__, 和__next__这两个特殊方法,实现了迭代器A。

2. 自定义一个可迭代器

2.1 实现迭代器

在Python中从头开始构建迭代器很容易。我们只需要实现这些方法__iter__()__next__()

  • __iter__()方法需要返回迭代器对象, 最简单直接返回self,也可以返回新的可迭代对象。如果需要,可以执行一些初始化
  • __next__()方法必须返回序列中的下一项。在到达终点时,以及在随后的调用中,它必须引发StopIteration

这里,我们展示了一个示例,通过定义一个迭代器,手动实现python的range方法:

class Range:
    
    def __init__(self,start,stop,step):
        self.start = start  
        self.stop = stop  
        self.step = step 
             
    
    def __iter__(self):
        self.value = self.start
        return self
    
    def __next__(self):
        # 1. 每执行一次next,需要返回一个值  
        # 2. 如果没有下一个值了,需要通过StopIteration 反馈异常
        if self.value < self.stop:
            old_value = self.value   
            self.value = self.value +self.step  
            return old_value
        else:
            raise StopIteration()

for i in Range(0,5,1):
    print(i)

输出结果
在这里插入图片描述

我们知道python 的range方法 有三个参数:start_value, stop_value和step,因此我们也定义这3个参数。

  • 首先定义类的__init__方法
  • 然后实现__iter__方法,并返回可迭代对象,这里返回了本身(slef)。其中在__iter__方法中,初始化了返回值self.value__iter__方法中,如果需要,可以执行一些初始化
  • 最后通过__next__方法,定义每一次迭代输出的值。当迭代完了,没有下一个值,通过raise StopIteration(), 反馈错误。

在for循环中,最后反馈的raise StopIteration() erro,会被for循环处理掉,因此我们看不到报错的提醒。

2.2 for 遍历迭代器的过程

在这里插入图片描述
通过单步调试,可以观察到如下执行顺序:

  • (1) 首先调用__init__方法,对成员变量进行初始化
  • (2) 紧接着,进入__iter__,获得一个迭代器对象self
  • (3) 最后进入__next__方法,执行循环迭代过程,每迭代一次返回相应的迭代结果。最后迭代会执行raise StopIteration()语句,此时程序结束(异常被for处理,所以没有显示出来)

可以看到所谓for循环,本质上是就是一次次的执行__next__方法的过程。for循环可等价如下代码:

r = Range(0,5,1)	  # __init__ 构造对象
iteration = iter(r)   # 执行__iter__,获得迭代器对象
#iteration =  r.__iter__()

next(iteration)      # 执行__next__
#iteration.__next__()
next(iteration)		 # 执行__next__
next(iteration)      # 执行__next__
next(iteration)      # 执行__next__
next(iteration)      # 执行__next__
try:
    next(iteration)
except StopIteration as e:
    pass
  • 最后一次会报StopIteration 异常,因此通过try except进行处理。
  • 注:`
    • iter(r) 就是 r.__iter__()实现的
    • next(iteration)就是通过iteration.__next__()实现的

3. yolov8 Dataset实现案例

class LoadImages:
   # YOLOv5 image/video dataloader, i.e. `python detect.py --source image.jpg/vid.mp4`
   def __init__(self, path, img_size=640, stride=32, auto=True, transforms=None, vid_stride=1):
       """Initialize instance variables and check for valid input."""
       if isinstance(path, str) and Path(path).suffix == '.txt':  # *.txt file with img/vid/dir on each line
           path = Path(path).read_text().rsplit()
       files = []
       for p in sorted(path) if isinstance(path, (list, tuple)) else [path]:
           p = str(Path(p).resolve())
           if '*' in p:
               files.extend(sorted(glob.glob(p, recursive=True)))  # glob
           elif os.path.isdir(p):
               files.extend(sorted(glob.glob(os.path.join(p, '*.*'))))  # dir
           elif os.path.isfile(p):
               files.append(p)  # files
           else:
               raise FileNotFoundError(f'{p} does not exist')

       images = [x for x in files if x.split('.')[-1].lower() in IMG_FORMATS]
       videos = [x for x in files if x.split('.')[-1].lower() in VID_FORMATS]
       ni, nv = len(images), len(videos)

       self.img_size = img_size
       self.stride = stride
       self.files = images + videos
       self.nf = ni + nv  # number of files
       self.video_flag = [False] * ni + [True] * nv
       self.mode = 'image'
       self.auto = auto
       self.transforms = transforms  # optional
       self.vid_stride = vid_stride  # video frame-rate stride
       if any(videos):
           self._new_video(videos[0])  # new video
       else:
           self.cap = None
       assert self.nf > 0, f'No images or videos found in {p}. ' \
                           f'Supported formats are:\nimages: {IMG_FORMATS}\nvideos: {VID_FORMATS}'

   def __iter__(self):
       """Returns an iterator object for iterating over images or videos found in a directory."""
       self.count = 0
       return self

   def __next__(self):
       """Iterator's next item, performs transformation on image and returns path, transformed image, original image, capture and size."""
       if self.count == self.nf:
           raise StopIteration
       path = self.files[self.count]

       if self.video_flag[self.count]:
           # Read video
           self.mode = 'video'
           for _ in range(self.vid_stride):
               self.cap.grab()
           ret_val, im0 = self.cap.retrieve()
           while not ret_val:
               self.count += 1
               self.cap.release()
               if self.count == self.nf:  # last video
                   raise StopIteration
               path = self.files[self.count]
               self._new_video(path)
               ret_val, im0 = self.cap.read()

           self.frame += 1
           # im0 = self._cv2_rotate(im0)  # for use if cv2 autorotation is False
           s = f'video {self.count + 1}/{self.nf} ({self.frame}/{self.frames}) {path}: '

       else:
           # Read image
           self.count += 1
           im0 = cv2.imread(path)  # BGR
           assert im0 is not None, f'Image Not Found {path}'
           s = f'image {self.count}/{self.nf} {path}: '

       if self.transforms:
           im = self.transforms(im0)  # transforms
       else:
           im = letterbox(im0, self.img_size, stride=self.stride, auto=self.auto)[0]  # padded resize
           im = im.transpose((2, 0, 1))[::-1]  # HWC to CHW, BGR to RGB
           im = np.ascontiguousarray(im)  # contiguous

       return path, im, im0, self.cap, s
  dataset = LoadImages(source, imgsz=imgsz, vid_stride=vid_stride)
   dataloader = build_dataloader(dataset, batch_size, workers, shuffle, rank)
  • 可以看到Dataset,也是通过一个迭代器实现,这样做的好处就是:需要时才会返回处理好的数据,而不需要一次性将整个集合加载到内存中,这样可以节省内存空间,也提高了数据处理的效率。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/347739.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用 Redis 的 List 数据结构实现分页查询的思路

假设有一个存储数据的 List&#xff0c;每个元素代表一个记录&#xff0c;例如 recordsList。 按页存储数据&#xff1a; 每页存储一定数量的记录。例如&#xff0c;第一页存储索引 0 到 N-1 的记录&#xff0c;第二页存储索引 N 到 2N-1 的记录&#xff0c;以此类推。 分页查…

文件上传到本地

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>上传文件</title> </head> <body><form action"/upload" method"post" enctype"multipart/form-data&…

小黑艰难的前端啃bug之路:内联元素之间的间隙问题

今天开始学习前端项目&#xff0c;遇到了一个Bug调了好久&#xff0c;即使margin为0&#xff0c;但还是有空格。 小黑整理&#xff0c;用四种方法解决了空白问题 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></tit…

部署TOMCAT详解

目录 一、Tomcat概述 1.1Tomcat简介 1.2、Tomcat历史 1.3Tomcat官网 二、部署单实例Tomcat 1.下载Tomcat包 2. 解压Tomcat包 3.配置环境变量 4.刷新环境变量 5.查看tomcat是否安装成功 6.启动Tomcat 三、Tomcat目录介绍 1、tomcat主目录介绍 2.webapps目录介绍 3…

量子网络是什么

量子网络是基于量子力学规律对量子信息进行存储、处理和传输的物理装置&#xff0c;是实现量子通讯和大规模量子计算的基础。清华大学研究团队利用同种离子的双类型量子比特编码&#xff0c;在国际上首次实现无串扰的量子网络节点&#xff0c;对未来实现量子通讯和大规模量子计…

Springboot集成规则引擎框架-LiteFlow

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一波电子书籍资料&#xff0c;包含《Effective Java中文版 第2版》《深入JAVA虚拟机》&#xff0c;《重构改善既有代码设计》&#xff0c;《MySQL高性能-第3版》&…

mysql调优-Join多种连接方式

简单嵌套循环连接 r为驱动表&#xff0c;s为匹配表&#xff0c;可以看到从r中分别取出每一个记录去匹配s表的列&#xff0c;然 后再合并数据&#xff0c;对s表进行r表的行数次访问&#xff0c;对数据库的开销比较大 索引嵌套循环连接 这个要求非驱动表&#xff08;匹配表s&…

Linux中目录的操作和文件属性获取(opendir、readdir、close函数的使用)

访问目录 opendir函数 #include <dirent.h> DIR *opendir(const char *name); DIR *fdopendir(int fd); 使用文件描述符&#xff0c;要配合open函数使用 DIR是用来描述一个打开的目录文件的结构体类型 成功时返回目录流指针&#xff1b;出错时返回NULLreaddir函数 #incl…

Adobe Media Encoder 2023下载安装教程,ME 2023安装教程,附安装包和工具,无套路,轻松搞的安装

前言 Adobe Media Encoder是一个视频和音频编码应用程序&#xff0c;可让针对不同应用程序和观众&#xff0c;以各种分发格式对音频和视频文件进行编码。包括专门设计的预设设置&#xff0c;以便导出与特定交付媒体兼容的文件&#xff0c;可以按适合多种设备的格式导出视频&am…

【医学图像数据增强】切割-拼接(CS-DA)

切割-拼接CS-DA CS-DA 核心思想自然图像和医学图像之间的关键差异CS-DA 步骤确定增强后的数据数量 代码复现 CS-DA 核心思想 论文链接&#xff1a;https://arxiv.org/ftp/arxiv/papers/2210/2210.09099.pdf 大多数用于医学分割的数据增强技术最初是在自然图像上开发的&#x…

Git 教程 | 将本地修改后的文件推送到 Github 指定远程分支上

Git 是一种分布式版本控制系统&#xff0c;用于敏捷高效地处理任何大小的项目。它是由 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的开源版本控制软件。Git 的本地克隆就是一个完整的版本控制存储库&#xff0c;无论脱机还是远程都能轻松工作。开发人员会在本地提交其工…

Maven进阶

学习目标 理解分模块开发的意义 能够使用聚合工程快速构建项目 能够使用继承简化项目配置 能够根据需求配置生产、开发、测试环境&#xff0c;并在各环境间切换运行 一、分模块开发与设计 1. 分模块开发的意义 问题导入 分模块开发对工程有什么好处&#xff1f; 模块拆分原…

接口测试时遇到接口加密了该如何处理?

对明文编码生成信息摘要&#xff0c;以防止被篡改。比如MD5使用的是Hash算法&#xff0c;无论多长的输入&#xff0c;MD5都会输出长度为128bits的一个串。摘要算法不要秘钥&#xff0c;客户端和服务端采用相同的摘要算法即可针对同一段明文获取一致的密文。 对称加密 对称加密…

VM下Unbunt虚拟机上网设置

系列文章目录 VM下Unbunt虚拟机上网设置 VM虚拟机上网设置 系列文章目录一、VM虚拟机上网设置 一、VM虚拟机上网设置 右击VM软件中你需要设置的虚拟机&#xff0c;选择设置 宿主机如果你用的是笔记本外加WIFI连接选择NAT网络模式 进入虚拟机看能否上网 不行的话&#xff0c;进…

计算机速成课Crash Course - 23. 屏幕 2D 图形显示

今天继续计算机速成课Crash Course的系列讲解。 更多技术文章&#xff0c;全网首发公众号 “摸鱼IT” 锁定 -上午11点 - &#xff0c;感谢大家关注、转发、点赞&#xff01; 计算机速成课Crash Course - 23. 屏幕& 2D 图形显示 (qq.com) 23. 屏幕& 2D 图形显示 这台…

【Go 快速入门】安装 Go 语言 | 开发工具 Goland | 第一个 Go 语言程序

文章目录 前言安装 Go 语言编译器 Goland运行 Go 程序补充 前言 本系列教程&#xff0c;目的是帮助一个有其他编程基础的 Go 语言小白快速入门 Go 语言&#xff0c;而非启发式学习。每篇幅保证不说废话&#xff0c;尽可能精炼总结&#xff0c;为上手后续的 Go 相关项目打下基础…

目标检测数据集 - 抽烟检测数据集下载「包含VOC、COCO、YOLO三种格式」

数据集介绍&#xff1a;抽烟检测数据集&#xff0c;真实合成场景高质量图片数据&#xff0c;涉及场景丰富&#xff0c;比如街景抽烟、写字楼抽烟、办公室抽烟、楼道抽烟、遮挡行人抽烟、严重遮挡行人抽烟数据&#xff1b;适用实际项目应用&#xff1a;公共场所监控或室内监控场…

2024年,每个人都应该知道的10个免费学习资源网站

在这个数字化的时代&#xff0c;有着许多宝贵的网络资源&#xff0c;提供了无限的学习机会&#xff0c;为每个人敞开了免费获取知识的大门。但很多人不知道它们的存在。今天分享10个免费且丰富的学习资源网站&#xff0c;希望对大家所有帮助。 1.freeCodeCamp 您可以免费学习…

<网络安全>《2 国内主要企业网络安全公司概览(二)》

4 北京天融信科技有限公司(简称天融信) 信息内容LOGO成立日期创始于1995年总部北京市海淀区上地东路1号院3号楼北侧301室背景民营企业是否上市天融信[002212]A股市值99亿主要产品网络安全大数据云服务员工规模6000多人简介天融信科技集团&#xff08;证券代码&#xff1a;0022…

支持向量机(Support Vector Machines)(需要优化)

1.优化目标 一个更加强大的算法广泛的应用于工业界和学术界&#xff0c;它被称为支持向量机(Support Vector Machine)。与逻辑回归和神经网络相比&#xff0c;支持向量机&#xff0c;或者简称 SVM&#xff0c;在学习复杂的非线性方程时提供了一种更为清晰&#xff0c;更加强大…