Python 中产出关键字的用法是什么? 它能做什么?

例如,我试图理解这个代码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_camedates 被调用时会怎样? 列表是否返回? 单一个元素吗? 是否再次调用? 以后的电话何时停止?


1. 本代码由Jochen Schulz(jrschulz)编写,他为公制空间制作了一个伟大的Python图书馆,与完整的源:模块mspace链接。


当前回答

虽然很多答案都表明你为什么用一个产量来创建发电机, 但对于产量来说却有更多的用途。 做一个共同的习惯很容易, 这样可以让信息在两个代码区块之间传递。 我不再重复任何已经给出的关于使用产量来创建发电机的好例子 。

为了帮助理解下一个代码中的产值, 您可以用手指通过任何有产值的代码来追踪循环。 每次你的手指碰到产量的代码时, 您必须等待下一个或发送输入。 调用下一个代码时, 您可以跟踪代码, 直到你到达产量... 产值右侧的代码被评估并返回到调用者... 然后您等待。 当调用下一个代码时, 您会通过代码执行另一个循环。 但是, 您会注意到, 产值也可以用一个发送方式来使用。 发送时, 将会将一个调用器的值发送到产值中 。 如果发送了, 则会收到发送的值, 然后将其从左侧吐出... , 然后通过代码的跟踪直到您再次到达产量( 返回最后的值, 如下一个调用 ) 。

例如:

>>> def coroutine():
...     i = -1
...     while True:
...         i += 1
...         val = (yield i)
...         print("Received %s" % val)
...
>>> sequence = coroutine()
>>> sequence.next()
0
>>> sequence.next()
Received None
1
>>> sequence.send('hello')
Received hello
2
>>> sequence.close()

其他回答

要理解它的产值函数,人们必须理解发电机是什么。此外,在理解发电机之前,你必须理解易用的东西。易用 : 易用 : 要创建列表, 您自然需要能够逐个读取每个元素。 逐项读取其项目的过程被称为迭代 :

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

My list 是可替换的。 当您使用列表理解值时, 您会创建一个列表, 因此该列表是可替换的 :

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

所有可用于... 的数据结构都是可循环的; 列表、 字符串、 文件...

这些惯用方法很方便,因为您可以随意阅读,但您可以将所有值存储在记忆中,当您有许多值时,这些值并不总是可取的。 生成器: 生成器 A 也是一种迭代器, 一种特殊的迭代器, 只能迭代一次。 生成器不会将所有值存储在记忆中, 而是在苍蝇上生成值 :

发电机:发电机、发电机、发电机发电,但不储存能源;)

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

只要使用 () 而不是 [] , 列表理解就会变成发电机理解。 但是, 由于发电机只能使用一次, 您无法在我的生成器中执行 i 第二次 : 生成器计算 0, 然后丢弃它, 然后计算 1, 最后一次计算 4 。 典型的黑色盲人打破玉米 。

产出关键字的使用方式与返回相同,但函数返回生成器。

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

这个例子本身是毫无用处的,但是当您需要函数返回大量数值,而只需要读一次,使用产量就方便了。

要掌握收益率,需要清楚的是,当函数被调用时,函数正文中写入的代码将不会运行。函数只返回生成对象。启动者可能会对此感到困惑。

第二,明白代码会从每次使用发电机时留下的代码中继续使用。

现在最困难的部分是:

第一次调用您函数所创建的生成器对象时, 它会运行函数中的代码, 从开始一直运行到产生, 然后返回循环的第一个值。 然后, 以后的每次调用都会运行您在函数中写入的循环的下一个迭代, 并返回下一个值。 这将一直持续到生成器被视为空, 当函数运行时没有被击中时该生成。 这可能是因为循环已经结束, 或者因为您不再满足于“ if/ else ” 。

个人理解 我希望帮助你!

收益率与返回率相似。区别是:

函数输出使函数可循环( 在以下示例中, 质数( n= 1) 函数成为可循环的 )。 它基本上意味着下次调用函数时, 它会从它离开的地方( 以产出表达式的线为后方) 继续 。

def isprime(n):
    if n == 1:
        return False
    for x in range(2, n):
        if n % x == 0:
            return False
    else:
        return True

def primes(n = 1):
   while(True):
       if isprime(n): yield n
       n += 1 

for n in primes():
    if n > 100: break
    print(n)

在上述例子中, 如果是inprime( n) 是真实的, 它会返回质号。 在下一个迭代中, 它会从下一行继续

n += 1  

通常情况下, 它会用来创建一个不起作用的代名词。 将“ ield” 当作您函数的附加件, 以及您作为数组的函数。 如果符合某些标准, 您可以在函数中添加此值, 使之成为代名词 。

arr=[]
if 2>0:
   arr.append(2)

def func():
   if 2>0:
      yield 2

两者的输出结果相同。

使用产量的主要优势是创建迭代器。 迭代器在即时计算时不会计算每个项目的价值。 它们只在您要求时才计算。 这被称为懒惰评价 。

这里所有的答案都很好,但其中只有一个(最受投票支持的)与你的代码如何运作有关。其他的与一般的发电机有关,也与它们如何运作有关。

所以,我不重复发电机是什么或产量是什么;我认为这些都包含在现有的答案中。然而,在花了几个小时试图理解一个与你的代码相似的代码之后,我将打破它是如何运作的。

您的代码绕过二进制树结构。 让我们以这棵树为例:

    5
   / \
  3   6
 / \   \
1   4   8

另一个简单的二进制搜索树的十字路口:

class Node(object):
..
def __iter__(self):
    if self.has_left_child():
        for child in self.left:
            yield child

    yield self.val

    if self.has_right_child():
        for child in self.right:
            yield child

执行代码在树形对象上,它执行__iter___这样:

def __iter__(self):

    class EmptyIter():
        def next(self):
            raise StopIteration

    if self.root:
        return self.root.__iter__()
    return EmptyIter()

候选人发言可用树上元素替换; Python 翻译为

it = iter(TreeObj)  # returns iter(self.root) which calls self.root.__iter__()
for element in it: 
    .. process element .. 

因为节点. _ iter_ 函数是一个生成器, 内部的代码按迭代执行 。 所以执行会是这样的 :

根元素是第一个; 检查它是否留下了孩子, 并且要循环它们( 因为我们叫它它 1 ) 。 它有一个孩子, 所以执行它。 给孩子自己。 左左为自己创建一个新的循环器 。 左是节点对象本身( it2) 。 左是同一逻辑 2 , 新的循环器已经创建( it3) 。 现在我们到达了树的左端 。 现在我们到达了树的左端。 它3 没有留下孩子, 所以它会继续下去并产生自我。 在下一个呼叫( it3) 时, 它会提高停止作用, 因为它没有正确的孩子( 到达函数的尽头, 但没有产生任何效果) 。 它1 和它2 仍然在活动 - 它们没有耗尽, 调用下一个( it2) 将产生值, 而不是提高停止作用 。 现在我们回到了它的上下文 2 , 并调下一个( it2) 继续它停止它的地方 : 在产生子声明之后 。 由于它没有更多的剩余孩子, 它会继续持续并产生自我 val 。 val 。

这里的渔获是,每次迭代都会产生次标准来绕过树,并保持当前迭代的状态。 一旦它到达终点,它就会绕过堆叠,并按正确的顺序返回值( 最小的收益值首先 ) 。

您的代码示例在一种不同的技术中做了类似的事情: 它为每个孩子输入了一个元素列表, 然后在下一个迭代中, 它弹出它, 并在当前对象上运行函数代码( 也就是自定义 ) 。

我希望这对这个传奇话题有一点帮助,我花了好几个小时来画这个过程来理解它。

python 的输出与返回语句类似,但有些差异除外。如果要从函数返回多个值,返回语句将把所有值都作为列表返回,并将其存储在调用符块的内存中。但如果我们不想使用额外的内存,会怎样?相反,我们需要在需要时从函数中获取该值。这是产出的来源。考虑以下函数:

def fun():
   yield 1
   yield 2
   yield 3

打电话的人是:

def caller():
   print ('First value printing')
   print (fun())
   print ('Second value printing')
   print (fun())
   print ('Third value printing')
   print (fun())

上述代码段(调用函数),如果调用,产出:-

First value printing
1
Second value printing
2
Third value printing
3

从上文可以看出, 产出返回其调用器的值, 但当函数再次调用时, 它不会从第一个语句开始, 而是从产出后右侧的语句开始。 在上述示例中, “ 第一值打印” 打印, 函数被调用。 1 被回传并打印。 然后, 打印“ 第二值打印” , 并再次调用有趣 () 。 它不打印 1 (第一个语句) , 而是返回 2 , 也就是说, 仅从产出 1 之后的语句 。 同样的程序会进一步重复 。