菜单

python中的yield关键字

2019年4月3日 - 金沙编程资讯

除了for循环取值,你也足以透过next()来取下八个值。

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

三. next()调用中的传参

参数值有注入的效劳,可改变上3个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为一,但在其次次调用next的时候one其实是undefined的,因为generator不会自行保存相应变量值,我们供给手动的钦命,那时two值为NaN,在第二次调用next的时候实施到yield
三 * two,通过传参将上次yield再次来到值two设为贰,得到结果

另3个板栗:

鉴于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数据传入

图片 1

小心代码的第一陆行,那里参数用了url变量,是为着和data数据做相比较

因为开头next()没有参数,尽管直接将url换到data的话,就会因为promise对象的数额判断
!rs == undefined 而reject

所以将第二陆行换来 cb(data || url);

图片 2

因而模拟的ajax输出,可探听到next的传参值,第二次在log输出的是 url =
‘url1’值,后续将data = ‘url一’传入req请求,在log中输出 data = ‘url1’值

 

迭代器

迭代器是访问集合内成分的壹种格局。迭代器对象从集合的率先个成分起首访问,直到全体的因素都被访问一次后终止。
能够运用工厂函数iter()重临2个迭代器。

>>> iter([1,2,3])
<listiterator object at 0x1100b6a50>
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

至于生成器的思维

(瞎掰的。。。。)生成器到底起到哪些啊作用呢,尽管生成三个生成器对象,而生成器对象自然是二个迭代器,所以能够那样说,生成器重回了三个足以用for循环遍历所以子项,能够用next()方法访问下三个子项,能够在走访时动态的变化数据而节省里部存款和储蓄器的对象。

 

叩问生成器的落到实处机制

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

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

1.迭代

在掌握生成器从前,先知道迭代。

 

基本功概念

有人大概会说,小编一直迭代,遍历多好,为啥要用生成器,然后去遍历生成器,那多难为。

2. 生成器

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

图片 3

图形源于Iterables vs. Iterators vs.
Generators

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

生成器是足以迭代的,不过你 只能读取它1次,因为它并不把拥有的值放在内部存款和储蓄器中,它是实时地转变数据,
能够用生成器表明式创制:

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()开头实时地扭转数据,并重返,由此生成器只可读取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语句,则赶回3个值,再实施的时候从上次停下来的地点初阶实践。yield语句保存了上次举办后的意况,下次执行不是从头起首,而是从上次的情状起首。

当调用my_gen()这一个函数的时候,函数内部的代码不会立马实施,而是回到3个生成器对象,当使用for循环举行遍历的时候,函数内部的代码开头履行,执行到yield表明式再次来到七个值,记录当前情景并截至,下二次的走访时再从那几个状态开端实施。

举几个不太方便的例证,普通的函数便是从未存档的游乐,只要游戏起先,就玩到结尾,下一次再玩依然从头发轫,而生成器就是加了存档,下次玩从上次存档的地点伊始

伍. 越来越多使用

更加多利用可参考 MDN –
Generator

答复题主的题材

生成器

# Here you create the method of the node object that will return the generator
def node._get_child_candidates(self, distance, min_dist, max_dist):
  # Here is the code that will be called each time you use the generator object:
  # If there is still a child of the node object on its left
  # AND if distance is ok, return the next child
  if self._leftchild and distance - max_dist < self._median:
      yield self._leftchild
  # If there is still a child of the node object on its right
  # AND if distance is ok, return the next child
  if self._rightchild and distance + max_dist >= self._median:
      yield self._rightchild
  # If the function arrives here, the generator will be considered empty
  # there is no more than two values: the left and the right children

# Create an empty list and a list with the current object reference
result, candidates = list(), [self]
# Loop on candidates (they contain only one element at the beginning)
while candidates:
    # Get the last candidate and remove it from the list
    node = candidates.pop()
    # Get the distance between obj and the candidate
    distance = node._get_dist(obj)
    # If distance is ok, then you can fill the result
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)
    # Add the children of the candidate in the candidates list
    # so the loop will keep running until it will have looked
    # at all the children of the children of the children, etc. of the candidate
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result

地点的代码有多少个好玩的地点

candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))

上边的代码会再次来到全部的生成器,但是while不断得发生新的生成器。

咱俩壹般那样用extend

>>> a = [1, 2]
>>> b = [3, 4]
>>> a.extend(b)
>>> print(a)
[1, 2, 3, 4]

在上头的代码中,传入了三个生成器,那样做有八个好处

  1. 不须求读五回数据
  2. 字节点不用都留存内部存款和储蓄器中

上面的代码是行得通的,因为python并不爱护传入的参数是还是不是是一个list。它关心传入的是还是不是2个迭代器,所以strings,lists,tuples,generators都以足以当作参数字传送入的!那称之为鸭子类型,也是python如此受欢迎的原因之壹。

 

1.3 迭代器

能够被next()函数调用并频频再次回到下2个值的目的称为迭代器:Iterator,迭代器其内达成了__iter__方法和__next__办法,for循环本质是经过调用可迭代对象的__iter__措施,该方法重临三个迭代器对象,再用__next__方法遍历成分

概念2个迭代器:

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请求的会遇见过异步调用的题材,为了保证调用顺序的没有错,一般我们会在回调函数中调用,也有用到1些新的缓解方案如Promise相关的技术。

在异步编制程序中,还有壹种常用的缓解方案,它就是Generator生成器函数。顾名思义,它是一个生成器,它也是3个状态机,内部装有值及相关的意况,生成器重临贰个迭代器Iterator对象,我们能够经过这么些迭代器,手动地遍历相关的值、状态,保障科学的施行顺序。

生成器

生成器是迭代器的一种,不过只可以被迭代一回。那是因为生成器并不会将具有的数额存在内部存款和储蓄器里,而是在行使的时候生成数据。

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

上面用[]转移了一个迭代器,而那里用()转变了一个生成器。
可是再实践

for i in mygenerator:
    print(i)

不会有别的输出,因为生成器只好选取二次。在此以前3次遍历中,生成器计算的到0,不存款和储蓄,然后总括获得一,不存款和储蓄,最后计算获得四。

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

阅读

一心知晓 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中调用3遍,简单的 yield
showNumbers()之后察觉并从未实行函数里面包车型客车yield 10+一

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

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

相关文章

发表评论

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

网站地图xml地图