365bet体育在线网投ES六笔记(5)– Generator生成器函数

瞩目,当函数中出现yield,该函数重回的就是一个生成器。不在是平凡函数。

1.3 迭代器

能够被next()函数调用并连发重回下一个值的靶子称为迭代器:Iterator,迭代器其内完成了__iter__方法和__next__主意,for循环本质是由此调用可迭代对象的__iter__格局,该办法再次来到2个迭代器对象,再用__next__办法遍历元素

概念七个迭代器:

class MyRange:
    def __init__(self, end):
        self.index = 0
        self.end = end

    def __iter__(self):
        return self

    def __next__(self):
        if self.index < self.end:
            val = self.index
            self.index += 1
            return val
        else:
            raise StopIteration()

my_range = MyRange(3)

print([i for i in my_range])

[0, 1, 2]

print([i for i in my_range])

[]

迭代器只可以迭代三次,每趟调用调用 next()
方法就会上前一步,不能后退,所以当迭代器迭代到最终时,就不得以重新利用,全部必要将迭代器和可迭代对象分别定义

修改上边包车型客车可迭代对象:

class MyRange:
    def __init__(self, end):
        self.end = end

    def __iter__(self):
        return MyIterator(self.end)

class MyIterator:
    def __init__(self, end):
        self.index = 0
        self.end = end

    def __iter__(self):
        return self    

    def __next__(self):
        if self.index < self.end:
            val = self.index
            self.index += 1
            return val
        else:
            raise StopIteration()

my_range = MyRange(3)

print([i for i in my_range])

[0, 1, 2]

print([i for i in my_range])

[0, 1, 2]

4. for…of循环代替.next()

除此之外使用.next()方法遍历迭代器对象外,通过ES6提供的新循环境景况势for…of也可遍历,但与next差异的是,它会忽略return重回的值,如

function* showNumbers() {
    yield 1;
    yield 2;
    return 3;
}

var show = showNumbers();

for (var n of show) {
    console.log(n) // 1 2
}

其它,处理for…of循环,具有调用迭代器接口的办法措施也可遍历生成器函数,如扩命宫算符…的运用

function* showNumbers() {
    yield 1;
    yield 2;
    return 3;
}

var show = showNumbers();

[...show] // [1, 2, length: 2]

生成器

生成器是迭代器的一种,不过只可以被迭代1次。那是因为生成器并不会将有着的数目存在内部存款和储蓄器里,而是在选取的时候生成数据。

>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
...     print(i)
0
1
4

上面用[]扭转了三个迭代器,而那里用()变化了二个生成器。
只是再履行

for i in mygenerator:
    print(i)

不会有其余输出,因为生成器只可以接纳贰次。此前三次遍历中,生成器计算的到0,不存储,然后总计获得壹,不存款和储蓄,最后计算获得肆。

答案:可以精晓yield是三个生成器;

至于生成器的研究

(瞎掰的。。。。)生成器到底起到怎么呢功用吧,固然生成一个生成器对象,而生成器对象自然是1个迭代器,所以可以那样说,生成器重临了一个能够用for循环遍历所以子项,能够用next()方法访问下1个子项,能够在拜访时动态的变通数据而节本省部存款和储蓄器的指标。

 

打探生成器的兑现机制

迭代意味着,调用可迭代对象的*iter()方法和迭代器的**next*()方法

除此之外for循环取值,你也得以通过next()来取下一个值。

阅读

完全知道 Python
迭代对象、迭代器、生成器
对 Python
迭代的深刻商讨
Python迭代器和生成器
3.
(译)Python关键字yield的解释(stackoverflow)
Python之列表生成式、生成器、可迭代对象与迭代器

五. 愈来愈多应用

更多选拔可参考 MDN –
Generator

可迭代对象

python中,1般可以被for巡回遍历的对象正是可迭代对象。
拥有__iter__()形式的对象称之为可迭代对象,__iter__()主意再次回到3个迭代器。

t = func(19)
t.next()

2. 生成器

生成器与可迭代对象、迭代器的涉及

365bet体育在线网投 1

图形来自Iterables vs. Iterators vs.
Generators

生成器对象,在每回调用它的next()方法时回来七个值,直到它抛出StopInteration。

生成器是足以迭代的,可是你 只好够读取它二遍,因为它并不把具备的值放在内部存款和储蓄器中,它是实时地扭转数据,
能够用生成器表明式成立:

my_generator = (x ** 2 for x in range(3))

my_generator

<generator object <genexpr> at 0x7f975b7a4af0>

for i in my_generator:
    print(i)

0
1
4

yield

能够写3个平日的包括yield语句的Python函数,Python会检验对yield的利用并将函数标记为3个生成器,当函数执行到yield语句时,像return语句那样重回三个值,但是解释器会保存对栈的引用,它会被用来在下3回调用next时上升函数。

def my_generator():
    yield 1
    yield 2
    yield 'a'
    yield 'generator'

g = my_generator()

g

<generator object my_generator at 0x7f975b7a4d58>

next(g)

1

next(g)

2

next(g)

'a'

next(g)

'generator'

next(g)

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

StopIteration                             Traceback (most recent call last)

<ipython-input-12-5f315c5de15b> in <module>()
----> 1 next(g)


StopIteration: 

地点的例子中,每一遍调用next()初叶实时地转移数据,并回到,由此生成器只可读取3次,上次推行读取的值在下次履行中就不能读取。当全部生成器的值都被读取后,在调用机会合世StopIteration的谬误。

def my_gen():
    for i in range(5):
        yield i ** 3

my_gen()

<generator object my_gen at 0x7f975ae15a40>

mygen = my_gen()

for i in mygen:
    print(i)

0
1
8
27
64

历次执行到yield语句,则赶回二个值,再履行的时候从上次停下来的地方开头执行。yield语句保存了上次执行后的气象,下次执行不是从头起头,而是从上次的景色起首。

当调用my_gen()那个函数的时候,函数内部的代码不会及时实施,而是重临2个生成器对象,当使用for循环举办遍历的时候,函数内部的代码开头实践,执行到yield表明式重临多少个值,记录当前景色并终止,下一次的走访时再从那一个意况开头推行。

举2个不太适宜的例证,普通的函数正是没有存档的嬉戏,只要游戏开端,就玩到结尾,下3回再玩依旧从头开端,而生成器正是加了存档,下次玩从上次存档的位置起初

2. yield和yield*

突发性,我们相会到yield之后跟了3个*号,它是什么,有何样用呢?

恍如于生成器前边的*号,yield前面包车型地铁星号也跟生成器有关,举个大栗子:

function* showWords() {
    yield 'one';
    yield showNumbers();
    return 'three';
}

function* showNumbers() {
    yield 10 + 1;
    yield 12;
}

var show = showWords();
show.next() // {done: false, value: "one"}
show.next() // {done: false, value: showNumbers}
show.next() // {done: true, value: "three"}
show.next() // {done: true, value: undefined}

增添了一个生成器函数,我们想在showWords中调用1回,简单的 yield
showNumbers()之后察觉并未执行函数里面包车型客车yield 拾+壹

因为yield只好维持原状地赶回右侧运算后值,但现行反革命的showNumbers()不是1般的函数调用,重返的是迭代器对象

为此换个yield* 让它自动遍历进该对象

function* showWords() {
    yield 'one';
    yield* showNumbers();
    return 'three';
}

function* showNumbers() {
    yield 10 + 1;
    yield 12;
}

var show = showWords();
show.next() // {done: false, value: "one"}
show.next() // {done: false, value: 11}
show.next() // {done: false, value: 12}
show.next() // {done: true, value: "three"}

365bet体育在线网投,要留意的是,那yield和yield*
只还好generator函数内部使用,壹般的函数Nelly用会报错

function showWords() {
    yield 'one'; // Uncaught SyntaxError: Unexpected string
}

固然如此换来yield*不会平素报错,但利用的时候依旧会有标题,因为’one’字符串中尚无Iterator接口,未有yield提供遍历

function showWords() {
    yield* 'one'; 
}

var show = showWords();

show.next() // Uncaught ReferenceError: yield is not defined

在爬虫开发中,大家通常供给请求两个地点,为了确定保证顺序,引进Promise对象和Generator生成器函数,看这几个大约的板栗:

var urls = ['url1', 'url2', 'url3'];

function* request(urls) {
    urls.forEach(function(url) {
        yield req(url);
    });

//     for (var i = 0, j = urls.length; i < j; ++i) {
//         yield req(urls[i]);
//     }
}

var r = request(urls);
r.next();

function req(url) {
    var p = new Promise(function(resolve, reject) {
        $.get(url, function(rs) {
            resolve(rs);
        });
    });

    p.then(function() {
        r.next();
    }).catch(function() {

    });
}

上述代码中forEach遍历url数组,匿名函数内部不能够使用yield关键字,改换成注释中的for循环就行了

Yield

有人恐怕会说,作者一直迭代,遍历多好,为啥要用生成器,然后去遍历生成器,那多劳神。

一.二 可迭代对象

能够平昔效果于for循环的靶子统称为可迭代对象:Iterable,可迭代对象1般都达成了__iter()__措施,可迭代对象通过其内建的方__iter()__归来二个迭代器对象。

a_iterable = [1, 2, 3]

a_iterator = iter(a_iterable)  # 将可迭代对象转化为迭代器

next(a_iterator)

1

next(a_iterator)

2

next(a_iterator)

3

叁. next()调用中的传参

参数值有注入的功效,可改变上2个yield的重回值,如

function* showNumbers() {
    var one = yield 1;
    var two = yield 2 * one;
    yield 3 * two;
}

var show = showNumbers();

show.next().value // 1
show.next().value // NaN
show.next(2).value // 6

第一遍调用next之后回到值one为1,但在其次次调用next的时候one其实是undefined的,因为generator不会活动保存相应变量值,大家供给手动的钦定,那时two值为NaN,在第一回调用next的时候实施到yield
三 * two,通过传参将上次yield重临值two设为贰,得到结果

另2个板栗:

出于ajax请求涉及到互连网,不佳处理,那里用了setTimeout模拟ajax的呼吁再次回到,按顺序实行,并传递每便回去的多寡

 1 var urls = ['url1', 'url2', 'url3'];
 2 
 3 function* request(urls) {
 4     var data;
 5 
 6     for (var i = 0, j = urls.length; i < j; ++i) {
 7         data = yield req(urls[i], data);
 8     }
 9 }
10 
11 var r = request(urls);
12 r.next();
13 
14 function log(url, data, cb) {
15     setTimeout(function() {
16         cb(url);
17     }, 1000);
18     
19 }
20 
21 
22 function req(url, data) {
23     var p = new Promise(function(resolve, reject) {
24         log(url, data, function(rs) {
25             if (!rs) {
26                 reject();
27             } else {
28                 resolve(rs);
29             }
30         });
31     });
32 
33     p.then(function(data) {
34         console.log(data);
35         r.next(data);
36     }).catch(function() {
37         
38     });
39 }

高达了按顺序请求多少个地点的意义,伊始直接r.next()无参数,后续通过r.next(data)将data数据传入

365bet体育在线网投 2

留意代码的第二陆行,这里参数用了url变量,是为着和data数据做相比较

因为开首next()未有参数,若是直接将url换来data的话,就会因为promise对象的多寡判断
!rs == undefined 而reject

所以将第1六行换到 cb(data || url);

365bet体育在线网投 3

经过模拟的ajax输出,可明白到next的传参值,第一次在log输出的是 url =
‘url一’值,后续将data = ‘url壹’传入req请求,在log中输出 data = ‘url壹’值

 

365bet体育在线网投 4

def func(num):
    n,a,b = 0,0,1
    while num > n:
        yield b  #阻断,返回b
        a,b = b,a + b
        n+=1

for i in  func(19): #func(19)是一个生成器,生成器只有调用时执行一次。所以这里用循环
    print i

1.迭代

在知道生成器从前,先明了迭代。

1. 声明

Generator的宣示方式接近1般的函数评释,只是多了个*号,并且一般能够在函数内见到yield关键字

function* showWords() {
    yield 'one';
    yield 'two';
    return 'three';
}

var show = showWords();

show.next() // {done: false, value: "one"}
show.next() // {done: false, value: "two"}
show.next() // {done: true, value: "three"}
show.next() // {done: true, value: undefined}

如上代码,定义了2个showWords的生成器函数,调用之后回来了叁个迭代器对象(即show)

调用next方法后,函数内推行第一条yield语句,输出当前的事态done(迭代器是或不是遍历达成)以及相应值(一般为yield关键字背后的演算结果)

每调用1次next,则实施二遍yield讲话,并在该处暂停,return完结未来,就退出了生成器函数,后续如若还有yield操作就不再履行了

迭代器

当你生成了三个list,能够一个接三个地拜会那个list中的元素,那种作为被号称迭代。

>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
...     print(i)
0
1
4

地点代码中的mylist正是3个迭代器。list类型是可迭代的。

>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
...     print(i)
0
1
4

在python中得以通过“for… in
…”
那种方法遍历的都以迭代器,像lists,strings,files…

迭代器是很好用的,因为你能够很便宜地遍历个中的成分。不过这个数据都是存在内部存款和储蓄器里的,当数据量相当大时,这种措施就不是十一分了不起了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注