python迭代器与生成器答案_史上最全 Python 迭代器与生成器

原标题:史上最全 Python 迭代器与生成器

作者:浪子燕青

链接:http://www.langzi.fun/迭代器与生成器.html

迭代器与可迭代对象

概念

迭代器:是访问数据集合内元素的一种方式,一般用来遍历数据,但是他不能像列表一样使用下标来获取数据,也就是说迭代器是不能返回的。

Iterator:迭代器对象,必须要实现next魔法函数

Iterable:可迭代对象,继承Iterator,必须要实现iter魔法函数

比如:

fromcollections importIterable,Iterator

a = [ 1, 2, 3]

print(isinstance(a,Iterator))

print(isinstance(a,Iterable))

返回结果:

False

True

在Pycharm中使用alt+b进去list的源码中可以看到,在list类中有iter魔法函数,也就是说只要实现了iter魔法函数,那么这个对象就是可迭代对象。

上面的例子中a是一个列表,也是一个可迭代对象,那么如何才能让这个a变成迭代器呢?使用iter()即可。

fromcollections importIterable,Iterator

a = [ 1, 2, 3]

a = iter(a)

print(isinstance(a,Iterator))

print(isinstance(a,Iterable))

print(next(a))

print( '----')

forx ina:

print(x)

返回结果:

True

True

1

----

2

3

可以看到现在a是可迭代对象又是一个迭代器,说明列表a中有iter方法,该方法返回的是迭代器,这个时候使用next就可以获取a的下一个值,但是要记住迭代器中的数值只能被获取一次。

梳理迭代器(Iterator)与可迭代对象(Iterable)的区别:

可迭代对象:继承迭代器对象,可以用for循环(说明实现了iter方法)

迭代器对象:可以用next获取下一个值(说明实现了next方法),但是每个值只能获取一次,单纯的迭代器没有实现iter魔法函数,所以不能使用for循环

只要可以用作for循环的都是可迭代对象

只要可以用next()函数的都是迭代器对象

列表,字典,字符串是可迭代对象但是不是迭代器对象,如果想变成迭代器对象可以使用iter()进行转换

Python的for循环本质上是使用next()进行不断调用,for循环的是可迭代对象,可迭代对象中有iter魔法函数,可迭代对象继承迭代器对象,迭代器对象中有next魔法函数

一般由可迭代对象变迭代器对象

可迭代对象

可迭代对象每次使用for循环一个数组的时候,本质上会从类中尝试调用iter魔法函数,如果类中有iter魔法函数的话,会优先调用iter魔法函数,当然这里切记iter方法必须要返回一个可以迭代的对象,不然就会报错。

如果没有定义iter魔法函数的话,会创建一个默认的迭代器,该迭代器调用getitem魔法函数,如果你没有定义iter和getitem两个魔法函数的话,该类型就不是可迭代对象,就会报错。

比如:

classs:

def__init__(self,x):

self.x = x

def__iter__(self):

returniter(self.x)

# 这里必须要返回一个可以迭代的对象

# def __getitem__(self, item):

# return self.x[item]

# iter和getitem其中必须要实现一个

a = s( '123')

# 这里的a就是可迭代对象

# 这里不能调用next(a)方法,因为没有定义

forx ina:

print(x)

这里把注释符去掉返回结果也是一样的,返回结果:

1

2

3迭代器对象

一开始提起,iter搭配Iterable做可迭代对象,next搭配Iterator做迭代器。next()接受一个迭代器对象,作用是获取迭代器对象的下一个值,迭代器是用来做迭代的,只会在需要的时候产生数据。

和可迭代对象不同,可迭代对象一开始是把所有的列表放在一个变量中,然后用getitem方法不断的返回数值,getitem中的item就是索引值。

但是next方法并没有索引值,所以需要自己维护一个索引值,方便获取下一个变量的位置。

classs:

def__init__(self,x):

self.x = x

# 获取传入的对象

self.index = 0

# 维护索引值

def__next__(self):

try:

result = self.x[self.index]

# 获取传入对象的值

exceptIndexError:

# 如果索引值错误

raiseStopIteration

# 抛出停止迭代

self.index += 1

# 索引值+1,用来获取传入对象的下一个值

returnresult

# 返回传入对象的值

a = s([ 1, 2, 3])

print(next(a))

print( '----------')

forx ina:

# 类中并没有iter或者getitem魔法函数,不能用for循环,会报错

print(x)

返回结果:

Traceback (most recent call last):

1

----------

File "C:/CODE/Python进阶知识/迭代协议/迭代器.py", line 34, in

forx ina:

TypeError: 's'object isnotiterable

上面一个就是完整的迭代器对象,他是根据自身的索引值来获取传入对象的下一个值,并不是像可迭代对象直接把传入对象读取到内存中,所以对于一些很大的文件读取的时候,可以一行一行的读取内容,而不是把文件的所有内容读取到内存中。

这个类是迭代器对象,那么如何才能让他能够使用for循环呢?那就让他变成可迭代对象,只需要在类中加上iter魔法函数即可。

classs:

def__init__(self,x):

self.x = x

# 获取传入的对象

self.index = 0

# 维护索引值

def__next__(self):

try:

result = self.x[self.index]

# 获取传入对象的值

exceptIndexError:

# 如果索引值错误

raiseStopIteration

# 抛出停止迭代

self.index += 1

# 索引值+1,用来获取传入对象的下一个值

returnresult

# 返回传入对象的值

def__iter__(self):

returnself

a = s([ 1, 2, 3])

print(next(a))

print( '----------')

forx ina:

print(x)

返回结果:

1

----------

2

3

可以看到这个时候运行成功,但是这个对象还是属于迭代器对象,因为在next获取下一个值会报错。

知识整理

根据上面的代码提示,得到规律:

iter让类变成可迭代对象,next让类变成迭代器(要维护索引值)。

可迭代对象可以用for循环,迭代器可以用next获取下一个值。

迭代器如果想要变成可迭代对象用for循环,就要在迭代器内部加上iter魔法函数

可迭代对象如果想要能用next魔法函数,使用自身类中的iter()方法即可变成迭代器对象

classs:

def__init__(self,x):

self.x = x

self.index = 0

def__next__(self):

try:

result = self.x[self.index]

exceptIndexError:

raiseStopIteration

self.index += 1

returnresult

classb:

def__init__(self,x):

self.x = x

def__iter__(self):

returns(self.x)

a = b([ 1, 2, 3])

forx ina:

print(x)

返回结果:

1

2

3

这个时候是不能再用next方法了,应为类b是一个可迭代对象,并非迭代器,这个时候不能用next方法,但是可以让类b继承类s,这样就能用next()方法获取下一个值,但是你的类b中要存在索引值,不然会报错,如下代码:

classs:

def__init__(self,x):

self.x = x

# 获取传入的对象

self.index = 0

# 维护索引值

def__next__(self):

try:

result = self.x[self.index]

# 获取传入对象的值

exceptIndexError:

# 如果索引值错误

raiseStopIteration

# 抛出停止迭代

self.index += 1

# 索引值+1,用来获取传入对象的下一个值

returnresult

# 返回传入对象的值

# def __iter__(self):

# return self

classb(s):

def__init__(self,x):

self.x = x

self.index = 0

def__iter__(self):

returns(self.x)

a = b([ 1, 2, 3])

print(next(a))

print(next(a))

返回结果:

1

2

可以这么做,但是没必要,因为这样违反了设计原则。

迭代器的设计模式

迭代器模式:提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部

表示。

迭代器的设计模式是一种经典的设计模式,根据迭代器的特性(根据索引值读取下一个内容,不一次性读取大量数据到内存)不建议将next和iter都写在一个类中去实现。

新建一个迭代器,用迭代器维护索引值,返回根据索引值获取对象的数值,新建另一个可迭代对象,使用iter方法方便的循环迭代器的返回值。

生成器

生成器:函数中只要有yield,这个函数就会变成生成器。每次运行到yield的时候,函数会暂停,并且保存当前的运行状态,返回返回当前的数值,并在下一次执行next方法的时候,又从当前位置继续往下走。

简单用法

举个例子:

defgen():

yield1

# 返回一个对象,这个对象的值是1

defret():

return1

# 返回一个数字1

g = gen()

r = ret()

print(g,r)

print(next(g))

返回结果:

1

1

可以看到return是直接返回数值1,yield是返回的一个生成器对象,这个对象的值是1,使用next(g)或者for x in g:print x 都是可以获取到他的内容的,这个对象是在python编译字节码的时候就产生。

defgen():

yield1

yield11

yield111

yield1111

yield11111

yield111111

# 返回一个对象,这个对象内的值是1和11,111...

defret():

return1

return3

# 第二个return是无效的

g = gen()

r = ret()

print(g,r)

print(next(g))

forx ing:

print(x)

返回结果:

1

1

11

111

1111

11111

111111

就像迭代器的特性一样,获取过一遍的值是没法再获取一次的,并且不是那种一次把所有的结果求出放在内存或者说不是一次性读取所有的内容放在内存中。

梳理特性:

使用yield的函数都是生成器函数

可以使用for循环获取值,也可以使用next获取生成器函数的值

原理

函数工作原理:函数的调用满足“后进先出”的原则,也就是说,最后被调用的函数应该第一个返回,函数的递归调用就是一个经典的例子。显然,内存中以“后进先出”方式处理数据的栈段是最适合用于实现函数调用的载体,在编译型程序语言中,函数被调用后,函数的参数,返回地址,寄存器值等数据会被压入栈,待函数体执行完毕,将上述数据弹出栈。这也意味着,一个被调用的函数一旦执行完毕,它的生命周期就结束了。

python解释器运行的时候,会用C语言当中的PyEval_EvalFramEx函数创建一个栈帧,所有的栈帧都是分配再堆内存上,如果不主动释放就会一直在里面。

Python 的堆栈帧是分配在堆内存中的,理解这一点非常重要!Python 解释器是个普通的 C 程序,所以它的堆栈帧就是普通的堆栈。但是它操作的 Python 堆栈帧是在堆上的。除了其他惊喜之外,这意味着 Python 的堆栈帧可以在它的调用之外存活。(FIXME: 可以在它调用结束后存活),这个就是生成器的核心原理实现。

Python脚本都会被python.exe编译成字节码的形式,然后python.exe再执行这些字节码,使用dis即可查看函数对象的字节码对象。

importdis

# 查看函数程序字节码

a = 'langzi'

print(dis.dis(a))

print( '-'* 20)

defsb(admin):

print(admin)

print(dis.dis(sb))

返回结果:

10LOAD_NAME 0(langzi)

# 加载名字 为langzi

2RETURN_VALUE

# 返回值

None

--------------------

150LOAD_GLOBAL 0( print)

# 加载一个print函数

2LOAD_FAST 0(admin)

# 加载传递参数为admin

4CALL_FUNCTION 1

# 调用这个函数

6POP_TOP

# 从栈的顶端把元素移除出来

8LOAD_CONST 0( None)

# 因为该函数没有返回任何值,所以加载的值是none

10RETURN_VALUE

# 最后把load_const的值返回(个人理解)

None

代码函数运行的时候,python将代码编译成字节码,当函数存在yield的时候,python会将这个函数标记成生成器,当调用这个函数的时候,会返回生成器对象,调用这个生成器对象后C语言中写的函数会记录上次代码执行到的位置和变量。

再C语言中的PyGenObject中有两个值,gi_frame(存储上次代码执行到的位置f_lasti的上次代码执行到的变量f_locals),gi_code(存储代码),使用dis也可以获取到上次代码执行的位置和值。

举个例子:

importdis

defgen():

yield1

yield2

return666

g = gen()

# g是生成器对象

print(dis.dis(g))

print( '*'* 10)

print(g.gi_frame.f_lasti)

# 这里还没有执行,返回的位置是-1

print(g.gi_frame.f_locals)

# 这里还没有执行,返回的对象是{}

next(g)

print( '*'* 10)

print(g.gi_frame.f_lasti)

print(g.gi_frame.f_locals)

返回结果:

110LOAD_CONST 1( 1)

# 加载值为1

2YIELD_VALUE

4POP_TOP

126LOAD_CONST 2( 2)

8YIELD_VALUE

10POP_TOP

1312LOAD_CONST 3( 666)

14RETURN_VALUE

None

**********

- 1

# 因为还没有执行,所以获取的行数为 -1

{}

**********

2

# 这里开始执行了第一次,获取的行数是2,2对应2 YIELD_VALUE就是前面加载的数值1

{}

# g.gi_frame.f_locals 是局部变量,你都没定义那么获取的结果自然是{},你只需在代码中加上user='admin',这里的{}就会改变。

生成器可以在任何时候被任何函数恢复执行,因为它的栈帧实际上不在栈上而是在堆上。生成器在调用调用层次结构中的位置不是固定的,也不需要遵循常规函数执行时遵循的先进后出顺序。因为这些特性,生成器不仅能用于生成可迭代对象,还可以用于实现多任务协作。

就是说只要拿到了这个生成器对象,就能对这个生成器对象进行控制,比如继续执行暂停等待,这个就是协程能够执行的理论原理。

应用场景

读取文件,使用open(‘xxx’).read(2019)//打开一个文件,每次读取2019个偏移量。文件a.txt是一行文字,但是特别长,这一行文字根据|符号分开,如何读取?

写入文件代码:

# -*- coding:utf-8 -*-

importrandom

importthreading

importstring

importtime

t1 = time.time()

defwrite(x):

withopen( 'a.txt', 'a+') asa:

a.write(x + '||')

defrun():

forx inrange( 10000000):

strs = str(random.randint( 1000, 2000)) +random.choice(string.ascii_letters)* 10

write(strs)

forx inrange( 10):

t = threading.Thread(target=run)

t.start()

t2 = time.time()

print(t2 - t1)

读取文件代码:

# -*- coding:utf-8 -*-

defreadbooks(f, newline):

# f为传入的文件名,newline为分隔符

buf = ""

# 缓存,处理已经读出来的数据量

while1:

whilenewline inbuf:

# 缓存中的数据是否存在分隔符

pos = buf.index(newline)

# 如果存在就找到字符的位置,比如0或者1或者2

yieldbuf[:pos]

# 暂停函数,返回缓存中的从头到字符的位置

buf = buf[pos + len(newline):]

# 缓存变成了,字符的位置到末尾

chunk = f.read( 2010* 10)

# 读取2010*10的字符

ifnotchunk:

# 已经读取到了文件结尾

yieldbuf

break

buf += chunk

# 加到缓存

withopen( 'a.txt', 'r') asf:

forline inreadbooks(f, '||'):

print(line)

●编号635,输入编号直达本文返回搜狐,查看更多

责任编辑:

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

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

相关文章

[性能测试] LoadRunner结果分析 – TPS

本文转载自:http://www.tuicool.com/articles/6z6vuy针对吞吐率和 TPS 的关系,这个在结果分析中如何使用,就个人经验和朋友讨论后,提出如下建议指导,欢迎同僚指正。相关定义响应时间 网络响应时间 应用程序响应时间响…

密码学电子书_密码学中的电子密码书(ECB)

密码学电子书This Electronic Code Book (ECB) is cryptography as a mode of operation for a block cipher, with the characters the main things that every feasible block of plaintext or an original text has a corresponding characteristic of ciphertext value and…

tsql是mysql中的吗_Mysql中的sql是如何执行的

MySQL中的SQL是如何执行的MySQL是典型的C/S架构,也就是Client/Server架构,服务器端程序使用的mysqld.整体的MySQL流程如下图所示:MySQL是有三层组成:连接层: 负责客户端与服务器端建立连接,客户端发送SQL至服务端;SQL层: 对SQL语句进行查询处理;存储引擎层: 与数据库文件打交道…

软件质量特性测试

针对软件质量特性进行测试,可以避免重大漏测,一般人我不告诉他。《软件工程—产品质量》(GB/T 16260-2006)中规定对软件的每个质量特性与子特性都有定义:一、功能性:是指当软件在指定条件下使用&#xff0c…

PHP array_pop()函数与示例

PHP array_pop()函数 (PHP array_pop() function) array_pop() function is used to delete/pop last element from the array. array_pop()函数用于从数组中删除/弹出最后一个元素。 Syntax: 句法: array_pop(array);Here, array is the input array, function w…

网站关停就没事了?5100万账户文件被盗

曾经是美国三大音乐视频文件共享软件之一的imesh,意外倒闭。而更意外的是,就在近日,imesh这款已经倒闭的软件,5100万账户开始在暗网被黑客拍卖。 Imesh这款软件是美国纽约的老牌音乐视频分享软件之一,早在2000年前便已…

数据库表设计索引外键设计_关于索引的设计决策 数据库管理系统

数据库表设计索引外键设计Introduction: 介绍: The attributes whose values are required inequality or range conditions and those that are keys or that participate in join conditions require access paths. 其值为必需的不等式或范围条件的属性以及作为键…

接口测试从零开始系列_mock技术使用

1、什么情况下会使用mock技术 (1)需要将当前被测单元和其依赖模块独立开来,构造一个独立的测试环境,不关注被测单元的依赖对象,只关注被测单元的功能逻辑 ----------比如被测代码中需要依赖第三方接口返回值进行逻辑处…

amie 规则挖掘_AMIE的完整形式是什么?

amie 规则挖掘AMIE:工程师协会的准会员 (AMIE: Associate Member of the Institution of Engineers) AMIE is an abbreviation of Associate Member of the Institution of Engineers. The Institution of Engineers India Limited (IEIL) provides this profession…

java 马克思_单链表-Java

public class SinglyListNode {int val;SinglyListNode next;SinglyListNode() {}SinglyListNode(int x) {this.val x;}}/*执行用时:12 ms, 在所有 Java 提交中击败了66.93%的用户内存消耗:39.5 MB, 在所有 Java 提交中击败了5.06%的用户*/class MyLink…

python的pass语句_Python | 演示pass语句的示例

python的pass语句python中的pass语句 (pass statement in python) "pass" is a type of null operation or null statement, when it executes nothing happens. It is used when you want do not want to write any code/statement to execute but syntactically a …

HDS:聚焦未来的投资“冻结”

一家日本IT网站报道的有关HDS冻结对高端存储产品的投资一事引发众议。让人陷入疑惑的这次声明就是,HDS认为单纯的阵列产品并非企业存储的未来。 6月1日,IT Pro Nikkei网站发布了一篇报道,内容援引HDS一份表示将冻结高端存储业务的简报。这引发…

java js对象转字符串数组_JS数组转字符串(3种方法)【转】

JavaScript 允许数组与字符串之间相互转换。其中 Array 方法对象定义了 3 个方法,可以把数组转换为字符串,如表所示。数组方法说明toString()将数组转换成一个字符串toLocalString()把数组转换成本地约定的字符串join()将数组元素连接起来以构建一个字符…

中美共建大数据创新研究中心

由贵阳市人民政府、工信部电子一所、美国加州大学伯克利分校合作共建的贵州伯克利大数据创新研究中心日前在贵阳揭牌。 据了解,贵州伯克利大数据创新研究中心将分两阶段建设。第一阶段,2016年9月份至2017年底,将重点完成“学龄儿童大数据分析…

Python中的__init__和self是做什么的?

The __init__ and self are two keywords in python, which performs a vital role in the application. __init__和self是python中的两个关键字,在应用程序中起着至关重要的作用。 To begin with, it is important to understand the concept of class and object…

Palo Alto Networks漏洞防护扩展至云端

中国北京,2016年4月12日 –下一代安全企业Palo Alto Networks?(纽交所代码:PANW)近日宣布进一步增强其下一代安全平台,扩展漏洞防护能力,以满足那些依赖云环境和SaaS应用的业务对安全的需求。 企业机构需要变得更加灵活和有竞争力…

java 嵌套调用_Java嵌套类的使用

嵌套类是指被定义在另一个类内部的类,它为外部类提供服务。嵌套类分四种:静态成员类、非静态成员类、匿名类和局部类。一、静态成员类与非静态成员类的区别?在什么情况下可以用静态成员类?我们知道在类的设计中,为了避…

c语言getenv函数_getenv()函数与C ++中的示例

c语言getenv函数C getenv()函数 (C getenv() function) getenv() function is a library function of cstdlib header. It is used to get the environment string. It accepts a parameter which is an environment variable name (platform dependent, it may either case s…

isless()函数与C ++中的示例

C isless()函数 (C isless() function) isless() function is a library function of cmath header, it is used to check whether the given first value is less than the second value. It accepts two values (float, double or long double) and returns 1 if the first …

停牌17个月 汉能薄膜真的要复牌了?

最近,停牌超过一年的汉能薄膜又有新进展。 10月7日,路透社引述知情人士的消息称,香港证监会或将允许汉能薄膜发电复牌,不过复牌的前提是需汉能将符合一些特定条件。 该消息人士透露,证监会告知汉能,若要恢复…