何为使用yieldPython 中的关键字?

比如说,我在试着理解这个代码1:

def _get_child_candidates(self, distance, min_dist, max_dist):
    if self._leftchild and distance - max_dist < self._median:
        yield self._leftchild
    if self._rightchild and distance + max_dist >= self._median:
        yield self._rightchild  

这就是打电话的人:

result, candidates = [], [self]
while candidates:
    node = candidates.pop()
    distance = node._get_dist(obj)
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result

当方法_get_child_candidates是否调用 ? 列表是否返回 ? 单元素 ? 是否又调用 ? 以后的呼叫何时停止 ?


1. 本代码由Jochen Schulz(jrschulz)编写,他为公制空间制作了一个伟大的Python图书馆。模块 m 空间.

当前回答

从方案拟订的角度来看,迭代器是按下列方式执行的:Tunks 缩图.

实施迭代机、发电机和用于同时执行的线性集合,等等,作为杜丘,一种用途发送到关闭对象的信件它有一个调度员, 和给“ 消息” 的发件人解答.

"下一个"是发送到结束处的电文,该电文由“创建者”创建。亚列"打电话。

执行此计算有很多方法。 我使用突变, 但可以通过返回当前值和下一个生成者( 生成者) 返回当前值和下一个生成者( 生成者) 来进行这种不突变的计算( 生成者)优惠透明Racket使用一些中间语言对初始方案进行一系列转换,其中之一是进行这种改写,使产量经营者与较简单的经营者以某种语言进行转换。

这是如何重写产量的演示, 它使用 R6RS 的结构, 但语义与 Python 的相同 。 这是相同的计算模式, 只需要修改语法, 才能使用 Python 的 产量重写 。

Welcome to Racket v6.5.0.3.

-> (define gen
     (lambda (l)
       (define yield
         (lambda ()
           (if (null? l)
               'END
               (let ((v (car l)))
                 (set! l (cdr l))
                 v))))
       (lambda(m)
         (case m
           ('yield (yield))
           ('init  (lambda (data)
                     (set! l data)
                     'OK))))))
-> (define stream (gen '(1 2 3)))
-> (stream 'yield)
1
-> (stream 'yield)
2
-> (stream 'yield)
3
-> (stream 'yield)
'END
-> ((stream 'init) '(a b))
'OK
-> (stream 'yield)
'a
-> (stream 'yield)
'b
-> (stream 'yield)
'END
-> (stream 'yield)
'END
->

其他回答

以下是一个简单的例子:

def isPrimeNumber(n):
    print "isPrimeNumber({}) call".format(n)
    if n==1:
        return False
    for x in range(2,n):
        if n % x == 0:
            return False
    return True

def primes (n=1):
    while(True):
        print "loop step ---------------- {}".format(n)
        if isPrimeNumber(n): yield n
        n += 1

for n in primes():
    if n> 10:break
    print "wiriting result {}".format(n)

产出:

loop step ---------------- 1
isPrimeNumber(1) call
loop step ---------------- 2
isPrimeNumber(2) call
loop step ---------------- 3
isPrimeNumber(3) call
wiriting result 3
loop step ---------------- 4
isPrimeNumber(4) call
loop step ---------------- 5
isPrimeNumber(5) call
wiriting result 5
loop step ---------------- 6
isPrimeNumber(6) call
loop step ---------------- 7
isPrimeNumber(7) call
wiriting result 7
loop step ---------------- 8
isPrimeNumber(8) call
loop step ---------------- 9
isPrimeNumber(9) call
loop step ---------------- 10
isPrimeNumber(10) call
loop step ---------------- 11
isPrimeNumber(11) call

我不是皮松开发商,但看似我yield持有程序流程的位置, 下一个循环从“ ield” 位置开始 。 它似乎正在等待这个位置, 就在那个位置之前, 返回一个外部值, 下次继续工作 。

这似乎是一个有趣和好的能力:

yield用于创建generator。如果将生成器视为一个迭代器,每个迭代都会给您带来价值。当您在循环中使用收益率时,会得到一个生成对象,您可以用该对象从循环中以迭接方式从循环中获取项目

在皮顿generators(一种特殊类型的iterators)用于产生一系列数值和yield关键字就和return生成功能关键字。

另一件有趣的事yield关键字在保存statea 发电机功能.

所以,我们可以设定number每次对一个不同的值generator产值。

以下是一个例子:

def getPrimes(number):
    while True:
        if isPrime(number):
            number = yield number     # a miracle occurs here
        number += 1

def printSuccessivePrimes(iterations, base=10):
    primeGenerator = getPrimes(base)
    primeGenerator.send(None)
    for power in range(iterations):
        print(primeGenerator.send(base ** power))

yield允许您更聪明地写字for- 通过将循环部分计入一个便于再利用的单独方法。

假设你需要环绕电子表格的所有非空白行,对每行都做一些事情。

for i, row in df.iterrows(): #from the panda package for reading excel 
  if row = blank: # pseudo code, check if row is non-blank...
    continue
  if past_last_row: # pseudo code, check for end of input data
    break
  #### above is boring stuff, below is what we actually want to do with the data ###
  f(row)

如果你需要打电话g(row)在一个类似的循环中,你可能会发现自己重复for语句加有效行的检查,这是枯燥、复杂和易出错的。我们不想重复(DRY 原则) 。

您想要将检查每个记录的代码与实际处理行的代码区分开来, 比如f(row)g(row) .

您可以做一个函数, 将 f() 作为输入参数, 但使用要简单得多yield在一个方法中做所有关于检查有效行以准备拨打 f () 的无聊内容:

def valid_rows():
  for i, row in df.iterrows(): # iterate over each row of spreadsheet
    if row == blank: # pseudo code, check if row is non-blank...
      continue
    if past_last_row: # pseudo code, check for end of input data
      break
    yield i, row

请注意,方法的每次调用将返回下一行,但如果所有行都读取,且for结束, 方法将return通常。下一次调用将开始新的for循环。

现在您可以在数据上写入迭代, 而不必重复对有效行进行无趣的检查( 现在根据自己的方法来计算) , 例如 :

for i, row in valid_rows():
  f(row)

for i, row in valid_rows():
  g(row)

nr_valid_rows = len(list(valid_rows()))

仅此而已。 请注意, 我还没有使用诸如 迭代器、 生成器、 协议、 共同常规等术语 。 我认为这个简单的例子 适用于我们日常的许多编码 。

理解什么yield确实,你必须明白什么是发电机发电机。在您能够理解发电机之前,您必须理解易可动的.

易变性

创建列表时,您可以逐项阅读其项目。逐项阅读其项目被称为迭代:

>>> mylist = [1, 2, 3]
>>> for i in mylist:
...    print(i)
1
2
3

mylist易 易 易 性。当您使用对列表的理解时,会创建列表,因此,可以循环:

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

能够使用的一切 " 。for... in..."是可循环的;lists, strings文档...

这些可替换的功能是实用的,因为您可以随心所欲地阅读,但您将所有值都存储在记忆中,当您拥有很多值时,这并不总是你想要的。

发电机发电机

发电机是迭代器,是一种可循环的您只能循环一次。发电机不会存储所有值的内存,它们会在飞上生成值:

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

除了你用过的一样()代替[]但是,你,你无法不能表现 表现表现for i in mygenerator第二次,因为发电机只能使用一次:它们计算0,然后忘记它,计算1,最后计算4,一个一个。

产量d

yield是一个关键字,它被像return,但该函数将返回一个发电机。

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

这是一个毫无用处的例子, 但当你知道你的功能会返回 一大堆的值时, 它就方便了, 你只需要读一次。

师傅yield你必须明白当您调用函数时,函数体中的代码不会运行。函数只返回生成对象, 这有点棘手 。

然后,你的代码会继续 从它每次离开的代码开始for使用发电机。

现在,硬的部分:

第一次for调用从您函数创建的生成器对象,它将运行您函数中的代码,从开始一直运行到点击yield,然后它返回循环的第一个值。然后,每次随后的呼叫将运行您在函数中写入的循环的再次迭代,然后返回下一个值。这将一直持续到发电机被视为空,当函数运行时没有打中yield。这可能是因为循环已经结束,或者因为你不再满足"if/else".


您的代码解释

发电机:

# Here you create the method of the node object that will return the generator
def _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 the 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 the 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 are 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 the distance is ok, then you can fill in the result
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)

    # Add the children of the candidate to the candidate's list
    # so the loop will keep running until it has 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 并不在意一种方法的论据是否是一个列表。 Python 期望它能用字符串、列表、图普勒和生成器来操作。 这叫做鸭字打字, 也是Python之所以如此酷的原因之一。 但是这是另一个故事, 另一个问题...

您可以在这里停下来,或者读一下,看一个生成器的先进使用:

控制发电机耗竭

>>> class Bank(): # Let's create a bank, building ATMs
...    crisis = False
...    def create_atm(self):
...        while not self.crisis:
...            yield "$100"
>>> hsbc = Bank() # When everything's ok the ATM gives you as much as you want
>>> corner_street_atm = hsbc.create_atm()
>>> print(corner_street_atm.next())
$100
>>> print(corner_street_atm.next())
$100
>>> print([corner_street_atm.next() for cash in range(5)])
['$100', '$100', '$100', '$100', '$100']
>>> hsbc.crisis = True # Crisis is coming, no more money!
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> wall_street_atm = hsbc.create_atm() # It's even true for new ATMs
>>> print(wall_street_atm.next())
<type 'exceptions.StopIteration'>
>>> hsbc.crisis = False # The trouble is, even post-crisis the ATM remains empty
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> brand_new_atm = hsbc.create_atm() # Build a new one to get back in business
>>> for cash in brand_new_atm:
...    print cash
$100
$100
$100
$100
$100
$100
$100
$100
$100
...

注:Python 3, 用于 Python 3, 使用print(corner_street_atm.__next__())print(next(corner_street_atm))

它可以对控制获取资源等各种事情有用。

义大便,你最好的朋友

Itertools 模块包含操作可替换文件的特殊功能 。 是否想要重复生成器? 连锁二生成器? 组值与单线串连接的嵌入列表中?Map / Zip不创建其它列表吗 ?

然后,就刚刚import 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)]

了解迭代的内部机制

迭迭代是一个过程,意味着可迭代(实施__iter__()和迭代器(执行__next__()循环是您可以从中获取迭代器的任何对象。迭代器是允许您在迭代器上迭代的对象。

这篇文章中更多关于如何如何for环环工作.