理解Python的迭代器美高梅4858官方网站

作者:编程技术
1 the_iter = iter([1,2,3])
2 
3 
4 for i in the_iter:
5     print(i)

 

let arr = [1,2,3,4]let arrEntires = arr.entries //{value: [0, 1], done: false}let arrKeys = arr.keys() //对于数组,索引值就是键值arrKeys.next() //{value: 0, done: false}let arrValues = arr.values //{value: 1, done: false}
 1 from collections import Iterable
 2 from collections import Iterator
 3 
 4 class peo():
 5 
 6     def __init__(self,x):
 7         self.x = x
 8     def __iter__(self):
 9         return self
10     def __next__(self):
11         return self
12 
13 p = peo([1,2,3,4])
14 
15 print(isinstance(p,Iterable))
16 print(isinstance(p,Iterator))

 

原因是因为,对于对象的遍历,需要考虑到遍历是对象自身的属性还是遍历对象自身上的可枚举属性还是遍历原型上的属性还是遍历原型上的可枚举属性还是连[Symbol.iterator]也希望遍历出来。鉴于各方意见不一,并且现有的遍历方式可以满足,于是标准组没有将[Symbol.iterator]加入。

我们可以使用next()不断获取Iterator对象的下一个元素,直到抛出StopIteration错误

上面的代码实现的是找到10以内的奇数,代码中的类名可以随便取,不是一定需要使用我上面提供的类名的。

下面是Map的迭代器接口使用:

    __iter__() 和__next__()这2个方法都实现了的,就是Iterator。只实现__iter__() 方法的就是iterable。

 

迭代器分为内部迭代器和外部迭代器。

 1 from collections import Iterable
 2 from collections import Iterator
 3 
 4 class peo():
 5 
 6     def __init__(self,x):
 7         self.x = x
 8     def __iter__(self):
 9         return self
10 
11 
12 p = peo([1,2,3,4])
13 
14 print(isinstance(p,Iterable)) #判断是否是Iterable对象

15 print(isinstance(p,Iterator))

Python

let arr = ['a', 'b'];let iter = arr[Symbol.iterator] // { value: 'a', done: false }iter.next() // { value: 'b', done: false }iter.next() // { value: undefined, done: true }

所以,Iterator对象肯定也是iterable对象,但iterable对象却不一定是Iterator对象。

L通过iter进行包装后设为I,I可以被next()用来查找下一个值,所以I是Iterator。

虽然Javascript许多内建的数据结构已经实现了该接口,还有些结构是没有迭代器接口的,那怎么办,我们需要写迭代器,那么就需要知道迭代器是如何工作的。下面代码实现的一个简单迭代器:

1 the_iter = iter([1,2,3])
2 
3 print(next(the_iter))
4 print(next(the_iter))
5 print(next(the_iter))
6 
7 print(next(the_iter))

对迭代器来讲,有一个__next()就够了。在你使用for 和 in 语句时,程序就会自动调用即将被处理的对象的迭代器对象,然后使用它的next__()方法,直到监测到一个StopIteration异常。

回忆在我们的javascript中,可遍历的结构以及方式有很多。JavaScript 原有的表示“集合”的数据结构,主要是数组,ES6 又添加了Map和Set,这样就有了四种数据集合,而遍历这四种结构都有不同的方法。举个栗子,服务端提供数据给前端,前端进行数据可视化工作,对数据进行遍历展示使用的for,但是由于业务的变化,使得后端返回的数据结构发生变化,返回对象或者是set,map,导致前端遍历代码大量重写。而迭代器的目的就是要标准化迭代操作。

  美高梅4858官方网站 1

 

可选的return

(二)for循环的工作方式

 

function *bar(){// ...}

美高梅4858官方网站 2

 

输出结果为:1,2,3

    Iterator对象(迭代器):除了可以用for循环外,还可以用next()不断获取下一个元素.

 

function f {console.log}f

美高梅4858官方网站 3

如果我们自己定义迭代器,只要在类里面定义一个 iter() 函数,用它来返回一个带 next() 方法的对象就够了。

上面代码中,throw方法的执行可以在某种情况下自动被调用,也可以手动调用。throw方法主要向迭代器报告一个异常/错误,一般配合生成器使用。

 1 the_iter = iter([1,2,3])
 2 
 3 
 4 
 5 while True:
 6     try:
 7         element = next(the_iter)
 8         print(element)
 9     except StopIteration:
10         # 如果抛出异常,退出循环
11         break

Python

let arr = [1,2,3];for{console.log;}

for循环的工作方式:上面这段代码实际上和下面这段代码是一样的

 

for {console.log// [1,2,3]}

    iterable对象(可迭代的对象):可以使用for循环,例如:字符串、列表 、字典 、集合等

 

生成器是一种特殊的函数形式,生成器函数的声明语法为:

__iter__() 和__next__()2个方法都实现的,才是迭代器:Iterator

可以直接作用于for循环的对象统称为可迭代对象(Iterable)。

entries() 返回一个遍历器对象,用来遍历[键名, 键值]组成的数组。 keys() 返回一个遍历器对象,用来遍历所有的键名。 values() 返回一个遍历器对象,用来遍历所有的键值。

(一)iterable对象和Iterator对象的区别

我们通过range来实现打印多少个元素,这里表示打印5个元素,返回结果和上面一致。

Set的迭代器使用实例

在上面的例子中,实际可以用for循环.

Python

如下可以看出Set的默认迭代器接口[Symblo.iterator]是values

 

for(let item of new Set{console.log// [1,2,3,4]}

直接上代码

for-of 循环完全删除了for循环中追踪集合索引的需要,更能专注于操作集合内容。

所有的Iterable均可以通过内置函数iter()来转变为Iterator。

//迭代器就是一个函数,也叫迭代器生成函数function Iterator{let curIndex = 0;let next = () => {return {value: o[curIndex],done: o.length ==   curIndex}}//返回迭代对象,该对象有next方法return {next}}let arr = [1,2]let oIt = Iterator;//{value:1,done:false}oIt.next();//{value:2,done:false}oIt.next();// {value: undefined, done: true}oIt.next();// {value: undefined, done: true}

如果在Iterator的__next__方法中没有实现StopIteration异常,那么则是表示的全部奇数,那么需要在调用的时候设置退出循环的条件。

for-of 循环首先会调用 arr 数组中Symbol.iterator 属性对象的函数,就会获取到该数组对应的迭代器,接下来 iterator.next()被调用,迭代器结果对象的 value 属性会被放入到变量 num 中。数组中的数据项会依次存入到变量num 中,直到迭代器结果对象中的 done 属性变成 true 为止,循环就结束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Iterable:
    def __iter__(self):
        return Iterator()
 
class Iterator:
    def __init__(self):
        self.start=-1
    def __next__(self):
        self.start =2
        if self.start >10:
            raise StopIteration
        return self.start
 
I = Iterable()
for i in I:
    print(i)

数组的迭代器使用实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
>>> from collections import Iterator, Iterable
>>> help(Iterator)
Help on class Iterator:
 
class Iterator(Iterable)
|  Method resolution order:
|      Iterator
|      Iterable
|      builtins.object  
|**注解:从这里可以看出Iterable继承自object, Iterator继承自Iterable。
|  Methods defined here:
|
|  __iter__(self)
|
|  __next__(self)
|      Return the next item from the iterator. When exhausted, raise StopIteration
......
>>> help(Iterable)
Help on class Iterable:
 
class Iterable(builtins.object)
|  Methods defined here:
|
|  __iter__(self)
......

ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”。就可以使用上述默认会调用Iterator函数的API,而如果该数据结构没有提供实现这个接口又该怎么样达到最大化的互操作性呢?那么就可以自己构建符合这个标准的迭代器。

 

Object.prototype[Symbol.iterator] = function () {let curIndex = 0;let next = () => {return {value: this[curIndex],done: this.length == curIndex  }}return {next,return() {console.logreturn {}}}}let obj = {0: 'a',1: 'b',2: 'c'}//自动调用---遇到对迭代器消耗提前终止的条件for  {if  {break} else {console.log}}//自动调用---抛出异常for  {if  {throw new Error} else {console.log}}//手动调用let ot = obj[Symbol.iterator]()console.log

可以被next()函数调用并不断返回下一个值的对象称为迭代器(Iterator)。

下面是Set的迭代器接口使用:

复制迭代器

迭代器是一次性消耗品,使用完了以后就空了,请看。

 

 

 

 

 

Python

 

1
2
3
4
5
6
7
8
9
10
>>> L=[1,2,3]
>>> I=iter(L)
>>> for i in I:
...     print(i, end='-')
...
1-2-3-
>>>next(I)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

当循环以后就殆尽了,再次使用调用时会引发StopIteration异常。

我们想通过直接赋值的形式把迭代器保存起来,可以下次使用。
但是通过下面的范例可以看出来,根本不管用。

 

 

 

 

 

Python

 

1
2
3
4
5
6
7
8
9
10
11
12
>>> I=iter(L)
>>> J=I
>>> next(I)
1
>>> next(J)
2
>>> next(I)
3
>>> next(J)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

那怎么样才能达到我们要的效果呢?

我们需要使用copy包中的deepcopy了,请看下面:

 

 

 

 

 

Python

 

1
2
3
4
5
6
7
8
9
>>> import copy
>>> I=iter(L)
>>> J=copy.deepcopy(I)
>>> next(I)
1
>>> next(I)
2
>>> next(J)
1

补充:迭代器不能向后移动, 不能回到开始。

所以需要做一些特殊的事情才能实现向后移动等功能。

以上代码均在Python 3.4 中测试通过。

调用迭代器函数,返回一个对象,该对象就是迭代器对象,对象上拥有next方法,每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。next()迭代

 

上面代码中,生成器函数没有过多的代码,只需要使用关键字yeild来返回每次next()的值。

 

遍历器对象除了必须具有next方法,还可以具有可选的return方法和throw方法。

 

ES6为迭代器引入了一个隐式的标准化接口。Javascript许多内建的数据结构,例如Array、Map、Set、String、TypedArray、函数的 arguments 对象、NodeList 对象都具备 Iterator 接口。可以通过在控制台打印一个Array实例,查看其原型上具有一个Symbol.iterator属性(Symbol.iterator其实是Symbol的简写,属性名是Symbol类型代表着这个属性的唯一以及不可重写覆盖),它就是迭代器函数,执行这个函数,就会返回一个迭代器对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Iterable:
    def __iter__(self):
        return Iterator()
 
class Iterator:
    def __init__(self):
        self.start=-1
    def __next__(self):
        self.start =2
        return self.start
 
I = Iterable()
for count, i in zip(range(5),I):    #也可以用内置函数enumerate来实现计数工作。
    print(i)

生成迭代器对象的方法

 

为什么对象没有内置迭代器接口

iterable需要包含有__iter()方法用来返回iterator,而iterator需要包含有next__()方法用来被循环

什么是迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> L = [1,2,3]
>>> [x**2 for x in L]
[1, 4, 9]
>>> next(L)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'list' object is not an iterator
>>> I=iter(L)
>>> next(I)
1
>>> next(I)
2    
>>> next(I)
3
>>> next(I)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

如何部署迭代器接口

 

迭代器就是为实现对不同集合进行统一遍历操作的一种机制,只要给需要遍历的数据结构部署Iterator接口,通过调用该接口,或者使用消耗该接口的API实现遍历操作。

 

在上面调用next方法的栗子中,需要注意的是:

 

内部迭代器:本身是函数,该函数内部定义好迭代规则,完全接受整个迭代过程,外部只需要一次调用。例如Array.prototype.forEach方法、jQuery.each都是内部迭代器。 外部迭代器:本身是函数,执行返回迭代对象,迭代下一个元素必须显式调用。使用forEach遍历,只可以一次性把数据全部拉取消耗,而迭代器可以用于以一次一步的方式控制行为,使得迭代过程更加灵活可控。

 

var it = {};it[Symbol.iterator] = function* () {yield 1;yield 2;yield 3;};//可以被...遍历,说明已经部署成功console.log// [1, 2, 3]let myIterator = it[Symbol.iterator]()console.log//{value: 1, done: false}console.log//{value: 2, done: false}console.log//{ value: 3, done: false }console.log//{ value: undefined, done: true }

当然,我们可以把这两个类合并在一起,这样实现程序的简练。
最终版本如下

下面是数组的迭代器接口使用:

Python

为什么要有迭代器

Python

Map 的默认迭代器接口[Symblo.iterator]是 entries;

  1. 内置函数iter()仅仅是调用了对象的__iter()方法,所以list对象内部一定存在方法iter__()
  2. 内置函数next()仅仅是调用了对象的__next()方法,所以list对象内部一定不存在方法next__(),但是Itrator中一定存在这个方法。
  3. for循环内部事实上就是先调用iter()把Iterable变成Iterator在进行循环迭代的。

在获得数组最后一位元素的时候,迭代器不会报告done:true,这时候需要再次调用next(),越过数组结尾的值,才能得到完成信号done:true。

Python

实现迭代器接口后,如何进行使用?

题外话:

let map = new Maplet mapEntires = map.entries //{value: [1, 2], done: false}let mapKeys = map.keys //{value: 1, done: false}let mapValues = map.values //{value: 2, done: false}

 

产生迭代器对象,我们可以通过定义迭代器函数来生产迭代器对象,还可以调用JavaScript在内置数据结构中定义好的迭代器函数来生产。除此之外,对于数组以及ES6新增的几个新的数据结构MAP、Set,这些集合不仅本身已部署迭代器接口,还提供了API方法来产生迭代器对象。ES6 的数组、Set、Map 都部署了以下三个方法,调用后都返回遍历器对象。

上面例子中,列表L可以被for进行循环但是不能被内置函数next()用来查找下一个值,所以L是Iterable。

除了上面在为对象添加遍历器生成函数的这种根据迭代器协议直接生成迭代器对象的方式外,还有什么方式可以生成迭代器对象呢?有,它是一种特殊的函数,叫生成器。

 

除了上面展示的for..of循环可以一个一个的消耗迭代器之外,还有其它ES6结构也可以用来消耗迭代器。例如spread运算符:

 

*前后可以有空格也可以没有空格。生成器函数的声明虽然和普通函数有区别,但是执行和普通函数一样,一样可以传参数。那它们的主要区别是什么呢?

 

通常情况下,在已经迭代完毕的迭代器对象上继续调用next方法会继续返回{value: undefined, done: true}而不会报错。

 

Map的迭代器使用实例

 

let arr = [1, 2, 3, 4, 5]var it = arr[Symbol.iterator]()//部分消耗var [x, y] = itconsole.log //打印1 2//完全消耗var [y, ...z] = itconsole.log //打印3 [4,5]

 

迭代器分类

 

生成器函数的执行会返回一个迭代器对象来控制该生成器函数执行其代码。因此,函数的执行变得可控。还可以在生成器中使用新的关键字yield,用来标示一个暂停点。迭代器除了可以控制函数执行外,还可以在每一次暂停中双向传递信息,暂停的时候生成器函数会返回一个值,恢复执行的时候迭代器可以通过向next方法传参向函数内部传递一个值。可以理解为多次传参,多个返回值。

 

迭代器使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Iterable:
    def __iter__(self):
        return self
    def __init__(self):
        self.start=-1
    def __next__(self):
        self.start =2
        if self.start >10:
            raise StopIteration
        return self.start
 
I = Iterable()
for i in I:
    print(i)

下面是一个为对象添加 Iterator 接口的例子:

 

let set = new Setlet setEntires = set.entries()//对于 Set,键名与键值相同。setEntires.next() //{value: [1, 1], done: false}let setKeys = set.keys //{value: 1, done: false}let setValues = set.values //{value: 1, done: false}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> L = [4,5,6]
>>> I = L.__iter__()
>>> L.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__next__'
>>> I.__next__()
4
>>> from collections import Iterator, Iterable
>>> isinstance(L, Iterable)
True
>>> isinstance(L, Iterator)
False
>>> isinstance(I, Iterable)
True
>>> isinstance(I, Iterator)
True
>>> [x**2 for x in I]    
[25, 36]

下面代码可以看出数组的for…of 遍历的默认迭代器接口是values

4.Iterator继承自Iterable,从下面的测试中可以很方便的看到Iterator包含__iter()和next()方法,而Iteratble仅仅包含iter__()。

迭代器模式

 

在接触迭代器之前,一起先了解什么是迭代器模式,回想一下我们生活中的事例。我们在参观景区需要买门票的时候,售票员需要做的事情,他会对排队购票的每一个人依次进行售票,对普通成人,对学生,对儿童都依次售票。售票员需要按照一定的规则,一定顺序把参观人员一个不落的售完票,其实这个过程就是遍历,对应的就是计算机设计模式中的迭代器模式。迭代器模式,提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。

除了像上述代码这样单独使用外,实现该接口的目的,就是为所有数据结构,提供一种统一的访问机制。实现了该接口,就可以调用ES6中新增的通过调用Iterator 接口实现的API,例如for..of就是典型的消耗迭代器的API。下面具体看看for..of的实现原理:

以及结构赋值也可以部分或者完全消耗一个迭代器:

return方法被定义为向迭代器发送一个信号,表明不会在消费者中再提取出任何值。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

for(let item of new Map{console.log// [1,2] [3,4]}

函数是一段执行特定任务的代码块,所以函数执行,相当于这一段代码块被执行。函数开始执行,在它执行完之前不会被打断,这段代码块将被全部执行完。在ES6引入生成器之前函数的确是这样执行的,但是前面介绍到外部迭代器可以相比内部迭代器对迭代过程进行控制,什么时候需要消耗,迭代器对象再next一下即可。类似迭代过程,函数的执行过程一样可以控制,函数可以不需要一次性执行完毕。

JavaScript 默认产生迭代器的API

在上面中,我们提及到对象没有设置可迭代的默认方法,是不可迭代对象,表现为其没有[Symbol.iterator]属性。虽然对象对我们来说,是键值存储的一种方式,尽管没有 map 那么好,key只可以是字符串,但是有的时候对象也是需要被迭代的,但是为什么不给对象设置可迭代的默认方法?

在上面,我们尝试过了为一个对象添加了Symbol.iterator方法,该方法就是该对象的遍历器生成函数,调用该函数会返回该对象的一个遍历器对象。

let obj = {0: 'a',1: 'b',2: 'c',length: 3,[Symbol.iterator]: function () {let curIndex = 0;let next = () => {return {value: this[curIndex],done: this.length == curIndex  }}return {next}}}for  {console.log}

如果把该对象的[Symbol.iterator]属性删除,那么就会报错Uncaught TypeError: obj is not iterable,告诉我们obj是不可被遍历。

本文由分分快三计划发布,转载请注明来源

关键词: 脚本 之家