菜单

ES陆笔记(伍)– Generator生成器函数

2019年4月3日 - 皇家赌场系统

时不时会映入眼帘,python函数中带有yield关键字,那么yield是怎么着,有啥样作用?

阅读

统统领悟 Python
迭代对象、迭代器、生成器
对 Python
迭代的递进钻研
Python迭代器和生成器
3.
(译)Python关键字yield的解释(stackoverflow)
Python之列表生成式、生成器、可迭代对象与迭代器

2. yield和yield*

突发性,大家会师到yield之后跟了八个*号,它是什么,有啥样用啊?

恍如于生成器前边的*号,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中调用一回,不难的 yield
showNumbers()之后察觉并未履行函数里面包车型地铁yield 拾+壹

因为yield只可以没有丝毫改变地赶回左侧运算后值,但近年来的showNumbers()不是相似的函数调用,重临的是迭代器对象

因此换个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"}

要留意的是,那yield和yield*
只可以在generator函数内部接纳,壹般的函数内使用会报错

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循环就行了

Itertools-最佳的对象

itertools模块中有诸多控制生成器的不二秘诀。

看上面的例子,看看四匹麦德林跑或者的次第组合

>>> horses = [1, 2, 3, 4]
>>> races = itertools.permutations(horses)
>>> print(races)
<itertools.permutations object at 0xb754f1dc>
>>> print(list(itertools.permutations(horses)))
[(1, 2, 3, 4), 
(1, 2, 4, 3), 
(1, 3, 2, 4), 
(1, 3, 4, 2), 
(1, 4, 2, 3), 
(1, 4, 3, 2), 
(2, 1, 3, 4), 
(2, 1, 4, 3), 
(2, 3, 1, 4), 
(2, 3, 4, 1), 
(2, 4, 1, 3), 
(2, 4, 3, 1), 
(3, 1, 2, 4), 
(3, 1, 4, 2), 
(3, 2, 1, 4), 
(3, 2, 4, 1), 
(3, 4, 1, 2), 
(3, 4, 2, 1), 
(4, 1, 2, 3), 
(4, 1, 3, 2), 
(4, 2, 1, 3), 
(4, 2, 3, 1), 
(4, 3, 1, 2), 
(4, 3, 2, 1)]

今非昔比的是履行进程中蒙受yield关键字,会阻断,yield
再次来到的是三个生成器。

1.迭代

在知晓生成器在此以前,先了然迭代。

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

除却使用.next()方法遍历迭代器对象外,通过ES陆提供的新循环形式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]

for循环遍历可迭代对象进程

  1. Python将对首要字in后的靶子调用iter函数获取迭代器
  2. 调用迭代器的next方法取得成分,直到抛出StopIteration相当。
  3. 对迭代器调用iter函数时将再次来到迭代器本人,所以迭代器也能够用于for语句中,不必要尤其处理。
    代码如下

it=iter(lst)
try:
      while True:
          val=it.next()
          print val
except
      StopIteration:
          pass

 

1.3 迭代器

能够被next()函数调用并不止重返下3个值的目的称为迭代器:Iterator,迭代器其内实现了__iter__方法和__next__艺术,for循环本质是由此调用可迭代对象的__iter__办法,该办法重临3个迭代器对象,再用__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]

壹、不难利用

领悟生成器的落到实处机制

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

t = func(19)
t.next()

至于生成器的思念

(瞎掰的。。。。)生成器到底起到何等呢效能吧,就算生成八个生成器对象,而生成器对象自然是一个迭代器,所以能够这么说,生成器再次来到了二个能够用for循环遍历所以子项,能够用next()方法访问下三个子项,能够在拜访时动态的变型数据而节本省存的靶子。

 

yield

Yield有点像return,不一致的是yield会重回一个生成器

>>> def createGenerator():
...     mylist = range(3)
...     for i in mylist:
...         yield i*i
...
>>> mygenerator = createGenerator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
...     print(i)
0
1
4

地点的那些例子未有何样用,不过当你通晓重临数据量非常的大并且只会被用到贰遍时,yield关键词就很有用了。
要理解yield,你无法不领会当重返生成器的函数被调用时,里面的代码实际上并不曾运营。这么些函数只是回来了八个生成器的靶子。那有点令人为难知晓。

在for循环中,这一个生成器才会被使用到。

当今跻身不便的片段:
for循环中,生成器第1次被调用到时,重临这些生成器的函数会顺序执行到yield部分,然后回到那么些轮回的第二个值。每一回调用到生成器,都会实施函数中的循环三遍,然后回来下一个值,直到未有值被重返。

当函数不再实行到yield的时候,生成器为空。这恐怕是循环甘休了只怕不再满意”if/else”判断。

瞩目,当函数中冒出yield,该函数再次来到的就是三个生成器。不在是日常函数。

2. 生成器

生成器与可迭代对象、迭代器的关联

皇家编程 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

能够写三个平凡的包涵yield语句的Python函数,Python会检查实验对yield的施用并将函数标记为三个生成器,当函数执行到yield语句时,像return语句那样再次回到二个值,然则解释器会保存对栈的引用,它会被用来在下二次调用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()伊始实时地转变数据,并赶回,因而生成器只可读取1次,上次实施读取的值在下次实践中就不或然读取。当1切生成器的值都被读取后,在调用机会出现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语句,则赶回1个值,再履行的时候从上次停下来的地点开端履行。yield语句保存了上次执行后的情事,下次执行不是从头开始,而是从上次的事态发轫。

当调用my_gen()那一个函数的时候,函数内部的代码不会立马实施,而是回到二个生成器对象,当使用for循环实行遍历的时候,函数内部的代码开首实践,执行到yield表明式再次回到1个值,记录当前景观并终止,下三次的拜会时再从这一个情景开头履行。

举三个不太对劲的事例,普通的函数就是从未存档的玩乐,只要游戏开端,就玩到结尾,下一回再玩依然从头开始,而生成器就是加了存档,下次玩从上次存档的地点起先

多元小说 — ES6笔记体系

正文

在stackoverflow中见到这么一个标题 What does the “yield” keyword do in
python

内部排行最高的答问对小编有一点都不小扶持,由此将其翻译下来分享给大家答案。
须臾间是译文:

要掌握什么是yield重要字,必须要掌握什么是生成器,而要精晓生成器,首先要领会什么是迭代器

 

1.1 迭代

1旦给定3个list或tuple,大家得以由此for循环来遍历那些list或tuple,那种遍历大家称为迭代(Iteration)

alist = [1, 2, 3, 4, 5]

for i in alist:
    print(i)

1
2
3
4
5

正如将列表中的成分通过for循环,遍历了整个alist列表,那种不另行地方便人民群众其内部的每二个子项的行为就是迭代。

 

基本功概念

相关文章

发表评论

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

网站地图xml地图