首页登陆欢迎您!
首页登陆 > 编程 > python生成器

python生成器

时间:2019-12-14

写在前言

1.迭代

在掌握生成器此前,先清楚迭代。

 

一再会映注重帘,python函数中带有yield关键字,那么yield是何许,有啥效果与利益?

1.1 迭代

若果给定二个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列表,这种不重复地方便人民群众其内部的每三个子项的表现便是迭代。

排山倒海小说 -- ES6笔记类别

 

1.2 可迭代对象

能够直接效果于for循环的指标统称为可迭代对象:Iterable,可迭代对象日常都完成了__iter()__艺术,可迭代对象通过其内建的方__iter()__回到二个迭代器对象。

a_iterable = [1, 2, 3]

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

next(a_iterator)

1

next(a_iterator)

2

next(a_iterator)

3

 

答案:能够精晓yield是四个生成器;

1.3 迭代器

能够被next(卡塔尔(قطر‎函数调用并不仅仅重返下三个值的靶子称为迭代器:Iterator,迭代器其内完结了__iter__方法和__next__形式,for循环本质是由此调用可迭代对象的__iter__主意,该措施重返贰个迭代器对象,再用__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]

接触过Ajax须求的会遇见过异步调用的标题,为了确认保证调用顺序的精确性,经常大家会在回调函数中调用,也许有用到部分新的消除方案如Promise相关的工夫。

在异步编制程序中,还会有生龙活虎种常用的解决方案,它便是Generator生成器函数。看名称就能够想到其意义,它是几个生成器,它也是二个状态机,内部装有值及相关的景况,生成器重回一个迭代器Iterator对象,大家得以因而这些迭代器,手动地遍历相关的值、状态,有限支持科学的实行顺序。

效用:蒙受yield关键字,函数会直接重返yield值,相当于return;分歧的是后一次调用的时候会从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(卡塔尔初始实时地扭转数据,并回到,由此生成器只可读取贰回,上次进行读取的值在后一次试行中就无法读取。当整个生成器的值都被读取后,在调用机会见世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(卡塔尔国这些函数的时候,函数内部的代码不会立刻实行,而是重返几个生成器对象,当使用for循环实行遍历的时候,函数内部的代码带头实施,奉行到yield表明式重返贰个值,记录当前场合并终止,下一回的访问时再从那么些情景伊始实行。

举一个不太切合的例子,普通的函数正是从未存档的玩耍,只要游戏以前,就玩到结尾,下二回再玩还是从头起头,而生成器正是加了存档,后一次玩从上次存档的地点起初

 

 

有关生成器的寻思

(瞎掰的。。。。)生成器到底起到哪些吧功用呢,纵然生成一个生成器对象,而生成器对象自然是二个迭代器,所以能够那样说,生成器再次来到了贰个得以用for循环遍历所以子项,能够用next(卡塔尔方法访问下三个子项,能够在探问时动态的变动数据而节本省部存储器的对象。

风华正茂、轻巧利用

生成器是如何?

阅读

一心精通 Python 迭代对象、迭代器、生成器
对 Python 迭代的深透钻研
Python迭代器和生成器
3. (译)Python关键字yield的解释(stackoverflow)
Python之列表生成式、生成器、可迭代对象与迭代器

1. 声明

Generator的宣示方式相近日常的函数注明,只是多了个*号,并且经常能够在函数内见到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}

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

调用next方法后,函数内实行第一条yield语句,输出当前的动静done(迭代器是还是不是遍历完毕)以至相应值(日常为yield关键字背后的运算结果)

每调用三遍next,则举办三遍yield话语,并在该处暂停,return完结之后,就退出了生成器函数,后续借使还会有yield操作就不再推行了

是可以迭代的,不过你 只能够读取它二回 ,因为它并不把装有的值放在内部存款和储蓄器中,它是实时地扭转数据:

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 10+1

因为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函数内部使用,平时的函数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循环就能够了

上一篇:Python压缩钦点文件及文件夹为zip 下一篇:没有了