Python的行为&x27;s";“收益率”;

Python的行为&x27;s";“收益率”;,python,yield,Python,Yield,我正在阅读python中的yield关键字,并试图理解如何运行此示例: def countfrom(n): while True: print "before yield" yield n n += 1 print "after yield" for i in countfrom(10): print "enter for loop" if i <= 20: print i e

我正在阅读python中的
yield
关键字,并试图理解如何运行此示例:

def countfrom(n):
    while True:
        print "before yield"
        yield n
        n += 1
        print "after yield"

for i in countfrom(10):
    print "enter for loop"
    if i <= 20:
        print i
    else:
        break
看起来yield将返回指定的值,并将继续运行函数直到结束(可能是在并行线程中)。我的理解正确吗


如果您能回答这个问题而不提及“生成器”,我将不胜感激,因为我试图一次理解一个生成器。

yield语句存储您生成的值,直到再次调用该函数为止。 因此,如果您(使用迭代器)调用该函数,它将再次运行该函数并给出值。
关键是它知道上次在哪里中断了

你可以把它想象成当遇到屈服的函数时,它只是“暂停”。下次调用它时,它将在
yield
后恢复,保持它离开时的状态

没有,只有一个线程

for循环的每次迭代都会运行
countFrom
函数,直到它产生某个结果或返回。在产生之后,for循环的主体再次运行,然后,当新的迭代开始时,
countFrom
函数恰好从它停止的地方开始,并再次运行,直到产生(或返回)

这个示例的修改版本将有助于更清楚地说明执行的路径

def countfrom(n):
    while n <= 12:
        print "before yield, n = ", n
        yield n
        n += 1
        print "after yield, n = ", n

for i in countfrom(10):
    print "enter for loop, i = ", i
    print i
    print "end of for loop iteration, i = ", i

函数
countfrom
不是在并行线程中运行的。这里发生的事情是,每当
for
-construct请求下一个值时,函数将执行,直到它到达
yield
语句。当需要之后的下一个值时,函数将从其停止的位置继续执行


虽然您要求不要提及“生成器”,但它们与
收益率
有着密切的联系,因此单独谈论它们是没有意义的。您的
countfrom
函数实际返回的是一个“生成器对象”。它在调用该对象后立即返回该对象,因此函数体根本不会执行,直到有东西(例如
for
-loop)使用其方法
.next()

从生成器请求值。如果不提及生成器,就无法解释
yield
语句的含义;这就像试图解释什么是石头而不提及岩石。也就是说:yield语句负责将正常函数转换为生成器

虽然您发现这里有很好的文档记录:

…其简要说明如下:

  • 调用使用yield语句的函数时,它返回一个“生成器迭代器”,具有
    .next()
    方法(iterable对象的标准)
  • 每次调用生成器的
    .next()
    方法时(例如,通过使用for循环迭代对象),都会调用函数,直到遇到第一个成品。然后暂停函数执行,并传递一个值作为
    .next()
    方法的返回值
  • 下次调用
    .next()
    时,函数将继续执行,直到下一次
    产生
    ,等等,直到函数返回某些内容
这样做的一些好处是:

  • 更少的内存使用,因为内存只分配给当前生成的值,而不是整个返回值列表(就像返回值列表一样)
  • “实时”结果返回,因为它们是生成的,可以在不等待生成结束的情况下传递给调用方(我用它来返回正在运行的进程的输出)

Python一直运行,直到达到
屈服
,然后停止并冻结执行。它不会继续运行。在下一次调用
countfrom

在不提及发电机的情况下,很容易说这一点,但事实是,产量和发电机是密不可分的。要真正理解它,你必须将它们视为同一个主题

通过以更手动的方式使用示例中的生成器,很容易向自己证明我(和其他人)所说的是正确的

一个函数,
yield
s而不是
return
ing真正返回一个生成器。然后,您可以通过调用
next
来使用该生成器。您感到困惑,因为您的循环正在为您处理背景中的所有内容

在这里,内部构件打开:

def countfrom(n):
    while n <= 12:
        print "before yield, n = ", n
        yield n
        n += 1
        print "after yield, n = ", n


your_generator = countfrom(10)
next(your_generator)
print "see the after yield hasn't shown up yet, it's stopped at the first yield"
next(your_generator)
print "now it woke back up and printed the after... and continued through the loop until it got to back to yield"
next(your_generator)
print "rinse and repeate"
def countfrom(n):

现在,我想我明白了,
yield
就像一个
return
,它将在下次调用时从
yield
行继续运行?不,但它会知道上次“n”的值是多少。在本例中,它似乎无关紧要,因为您可以将n作为一个参数传递,但稍后它会变得非常有用。我想我误解了您的评论。您的想法是正确的(对不起),看起来这里的生成器有点混乱(我也花了一些时间才完全理解它们):状态不是在多次调用函数之间保持的,而是在调用返回的生成器对象的.next()方法之间保持的。当然,这是通过对其返回值进行迭代来完成的。因此,收益率的工作方式类似于
返回
,它将从该行继续,在下次调用时保留该值?不,您不需要再次调用函数来恢复,您必须调用
next()返回的生成器对象上的
方法。@redShadow但是在我的示例中没有调用
next()
,那么它是如何恢复的呢?@Tom Brito,
next()
是由
for
循环在引擎盖下执行的。也就是说,“iterable”对象只是一个具有
next()
方法的对象,该方法应为其包含的每个项返回一个值,并在最后一个项被删除时引发异常reached@Tom:解释
下一步
需要使用您没有使用的单词
before yield, n =  10
enter for loop, i =  10
10
end of for loop iteration, i =  10
after yield, n =  11
before yield, n =  11
enter for loop, i =  11
11
end of for loop iteration, i =  11
after yield, n =  12
before yield, n =  12
enter for loop, i =  12
12
end of for loop iteration, i =  12
after yield, n =  13
def countfrom(n):
    while n <= 12:
        print "before yield, n = ", n
        yield n
        n += 1
        print "after yield, n = ", n


your_generator = countfrom(10)
next(your_generator)
print "see the after yield hasn't shown up yet, it's stopped at the first yield"
next(your_generator)
print "now it woke back up and printed the after... and continued through the loop until it got to back to yield"
next(your_generator)
print "rinse and repeate"