提高你的Python: 解释‘yield’和‘Generators(生成器)’ 已翻译 100%

renwofei423 投递于 2013/04/12 10:57 (共 16 段, 翻译完成于 04-18)
阅读 63685
收藏 80
28
加载中

Prior to beginning tutoring sessions, I ask new students to fill out a brief self-assessment where they rate their understanding of various Python concepts. Some topics ("control flow with if/else" or "defining and using functions") are understood by a majority of students before ever beginning tutoring. There are a handful of topics, however, that almost all students report having no knowledge or very limited understanding of. Of these, "generatorsand theyieldkeyword" is one of the biggest culprits. I'm guessing this is the case for most novice Python programmers.

Many report having difficulty understandinggeneratorsand theyieldkeyword even after making a concerted effort to teach themselves the topic. I want to change that. In this post, I'll explain what theyieldkeyword does, why it's useful, and how to use it.

Note: In recent years, generators have grown more powerful as features have been added through PEPs. In my next post, I'll explore the true power ofyieldwith respect to coroutines, cooperative multitasking and asynchronous I/O (especially their use in the "tulip" prototype implementation GvR has been working on). Before we get there, however, we need a solid understanding of how theyieldkeyword andgeneratorswork.

已有 2 人翻译此段
我来翻译

Coroutines and Subroutines

When we call a normal Python function, execution starts at function's first line and continues until areturnstatement,exception, or the end of the function (which is seen as an implicitreturn None) is encountered. Once a function returns control to its caller, that's it. Any work done by the function and stored in local variables is lost. A new call to the function creates everything from scratch.

This is all very standard when discussing functions (more generally referred to as subroutines) in computer programming. There are times, though, when it's beneficial to have the ability to create a "function" which, instead of simply returning a single value, is able to yield a series of values. To do so, such a function would need to be able to "save its work," so to speak.

I said, "yield a series of values" because our hypothetical function doesn't "return" in the normal sense.returnimplies that the function is returning control of execution to the point where the function was called. "Yield," however, implies that the transfer of control is temporary and voluntary, and our function expects to regain it in the future.

已有 1 人翻译此段
我来翻译

In Python, "functions" with these capabilities are calledgenerators, and they're incredibly useful.generators(and theyieldstatement) were initially introduced to give programmers a more straightforward way to write code responsible for producing a series of values. Previously, creating something like a random number generator required a class or module that both generated values and kept track of state between calls. With the introduction ofgenerators, this became much simpler.

To better understand the problemgeneratorssolve, let's take a look at an example. Throughout the example, keep in mind the core problem being solved: generating a series of values.

Note: Outside of Python, all but the simplestgeneratorswould be referred to as coroutines. I'll use the latter term later in the post. The important thing to remember is, in Python, everything described here as acoroutineis still agenerator. Python formally defines the termgenerator;coroutineis used in discussion but has no formal definition in the language.

已有 1 人翻译此段
我来翻译

Example: Fun With Prime Numbers

Suppose our boss asks us to write a function that takes alistofints and returns some Iterable containing the elements which are prime1 numbers.

Remember, an Iterable is just an object capable of returning its members one at a time.

"Simple," we say, and we write the following:

def get_primes(input_list):
    result_list = list()
    for element in input_list:
        if is_prime(element):
            result_list.append()

    return result_list

# or better yet...

def get_primes(input_list):
    return (element for element in input_list if is_prime(element))

# not germane to the example, but here's a possible implementation of
# is_prime...

def is_prime(number):
    if number > 1:
        if number == 2:
            return True
        if number % 2 == 0:
            return False
        for current in range(3, int(math.sqrt(number) + 1), 2):
            if number % current == 0: 
                return False
        return True
    return False

Eitheris_primeimplementation above fulfills the requirements, so we tell our boss we're done. She reports our function works and is exactly what she wanted.

已有 1 人翻译此段
我来翻译

Dealing With Infinite Sequences

Well, not quite exactly. A few days later, our boss comes back and tells us she's run into a small problem: she wants to use ourget_primesfunction on a very large list of numbers. In fact, the list is so large that merely creating it would consume all of the system's memory. To work around this, she wants to be able to callget_primeswith astartvalue and get all the primes larger thanstart(perhaps she's solving Project Euler problem 10).

Once we think about this new requirement, it becomes clear that it requires more than a simple change toget_primes. Clearly, we can't return a list of all the prime numbers fromstartto infinity (operating on infinite sequences, though, has a wide range of useful applications). The chances of solving this problem using a normal function seem bleak.

已有 1 人翻译此段
我来翻译

Before we give up, let's determine the core obstacle preventing us from writing a function that satisfies our boss's new requirements. Thinking about it, we arrive at the following: functions only get one chance to return results, and thus must return all results at once. It seems pointless to make such an obvious statement; "functions just work that way," we think. The real value lies in asking, "but what if they didn't?"

Imagine what we could do ifget_primescould simply return the next value instead of all the values at once. It wouldn't need to create a list at all. No list, no memory issues. Since our boss told us she's just iterating over the results, she wouldn't know the difference.

已有 1 人翻译此段
我来翻译

Unfortunately, this doesn't seem possible. Even if we had a magical function that allowed us to iterate fromntoinfinity, we'd get stuck after returning the first value:

def get_primes(start):
    for element in magical_infinite_range(start):
        if is_prime(element):
            return element
Imagineget_primesis called like so:
def solve_number_10():
    # She *is* working on Project Euler #10, I knew it!
    total = 2
    for next_prime in get_primes(3):
        if next_prime < 2000000:
            total += next_prime
        else:
            print(total)
            return

Clearly, inget_primes, we would immediately hit the case wherenumber = 3and return at line 4. Instead ofreturn, we need a way to generate a value and, when asked for the next one, pick up where we left off.

Functions, though, can't do this. When theyreturn, they're done for good. Even if we could guarantee a function would be called again, we have no way of saying, "OK, now, instead of starting at the first line like we normally do, start up where we left off at line 4." Functions have a singleentry point: the first line.

已有 1 人翻译此段
我来翻译

Enter the Generator

This sort of problem is so common that a new construct was added to Python to solve it: thegenerator. Agenerator"generates" values. Creatinggeneratorswas made as straightforward as possible through the concept ofgenerator functions, introduced simultaneously.

Agenerator functionis defined like a normal function, but whenever it needs to generate a value, it does so with theyieldkeyword rather thanreturn. If the body of adefcontainsyield, the function automatically becomes agenerator function(even if it also contains areturnstatement). There's nothing else we need to do to create one.

generator functionscreategenerator iterators. That's the last time you'll see the termgenerator iterator, though, since they're almost always referred to as "generators". Just remember that ageneratoris a special type ofiterator. To be considered aniterator,generatorsmust define a few methods, one of which is__next__(). To get the next value from agenerator, we use the same built-in function as foriterators:next().

已有 1 人翻译此段
我来翻译

This point bear repeating: to get the next value from agenerator, we use the same built-in function as foriterators:next().

(next()takes care of calling the generator's__next__()method). Since ageneratoris a type ofiterator, it can be used in aforloop.

So whenevernext()is called on agenerator, thegeneratoris responsible for passing back a value to whomever callednext(). It does so by callingyieldalong with the value to be passed back (e.g.yield 7). The easiest way to remember whatyielddoes is to think of it asreturn(plus a little magic) forgenerator functions.**

Again, this bears repeating: yieldis justreturn(plus a little magic) forgenerator functions.

Here's a simplegenerator function:

>>> def simple_generator_function():
>>>    yield 1
>>>    yield 2
>>>    yield 3
And here are two simple ways to use it:
>>> for value in simple_generator_function():
>>>     print(value)
1
2
3
>>> our_generator = simple_generator_function()
>>> next(our_generator)
1
>>> next(our_generator)
2
>>> next(our_generator)
3

已有 1 人翻译此段
我来翻译

Magic?

What's the magic part? Glad you asked! When agenerator functioncallsyield, the "state" of thegenerator functionis frozen; the values of all variables are saved and the next line of code to be executed is recorded untilnext()is called again. Once it is, thegenerator functionsimply resumes where it left off. Ifnext()is never called again, the state recorded during theyieldcall is (eventually) discarded.

Let's rewriteget_primesas agenerator function. Notice that we no longer need themagical_infinite_rangefunction. Using a simplewhileloop, we can create our own infinite sequence:

def get_primes(number):
    while True:
        if is_prime(number):
            yield number
        number += 1
已有 1 人翻译此段
我来翻译
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(15)

m
mx9999
for power in range(iterations)中的iterations是传入一个迭代器吗,要是一个迭代器为啥要加range啊,请赐教,并举一个iterations的示例
chirnson
chirnson
能讲讲,生成器与协程的关系吗? 协程的原理,适合什么样的场景
renwofei423
renwofei423

引用来自“LinuxQueen”的评论

引用来自“se77en.cc”的评论

引用来自“renwofei423”的评论

引用来自“se77en.cc”的评论

引用来自“se77en.cc”的评论

引用来自“renwofei423”的评论

引用来自“LinuxQueen”的评论

引用来自“renwofei423”的评论

引用来自“crab2313”的评论

引用来自“renwofei423”的评论

引用来自“crab2313”的评论

一楼翻译太奇芭了

估计是直接google翻译的。。。

我已经重翻了,这家伙估计来捣乱的

刚刚我也重新翻译了一遍,发上来才发现你已经翻译了,还翻译得更好=。=
晕死。。。哈哈

确实不错,顶一下。
我也新翻译了几段,不过只能保证语句通顺,技术上没有问题。

你也不错 哈哈

大家看这篇吧http://blog.jobbole.com/28506/ 来自stackoverflow上e-satis同学神一般的回复http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained

还有这篇http://blog.jobbole.com/21351/ 同样来自stackoverflow,http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python 大家看看收藏和评分就知道好坏了

好坏?
我提交的这篇文章来自Python-weekly推荐哦

这篇也不错~

这篇更详细一些,不过也更罗嗦一些,还是挺好的。应该还有第二部分吧?顺便说一下,oschina的回复展示也太难看了。

应该有后续,不过作者还没有写出来。

传说中的盖楼嘛
zicode
zicode

引用来自“se77en.cc”的评论

引用来自“renwofei423”的评论

引用来自“se77en.cc”的评论

引用来自“se77en.cc”的评论

引用来自“renwofei423”的评论

引用来自“LinuxQueen”的评论

引用来自“renwofei423”的评论

引用来自“crab2313”的评论

引用来自“renwofei423”的评论

引用来自“crab2313”的评论

一楼翻译太奇芭了

估计是直接google翻译的。。。

我已经重翻了,这家伙估计来捣乱的

刚刚我也重新翻译了一遍,发上来才发现你已经翻译了,还翻译得更好=。=
晕死。。。哈哈

确实不错,顶一下。
我也新翻译了几段,不过只能保证语句通顺,技术上没有问题。

你也不错 哈哈

大家看这篇吧http://blog.jobbole.com/28506/ 来自stackoverflow上e-satis同学神一般的回复http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained

还有这篇http://blog.jobbole.com/21351/ 同样来自stackoverflow,http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python 大家看看收藏和评分就知道好坏了

好坏?
我提交的这篇文章来自Python-weekly推荐哦

这篇也不错~

这篇更详细一些,不过也更罗嗦一些,还是挺好的。应该还有第二部分吧?顺便说一下,oschina的回复展示也太难看了。
se77en
se77en

引用来自“renwofei423”的评论

引用来自“se77en.cc”的评论

引用来自“se77en.cc”的评论

引用来自“renwofei423”的评论

引用来自“LinuxQueen”的评论

引用来自“renwofei423”的评论

引用来自“crab2313”的评论

引用来自“renwofei423”的评论

引用来自“crab2313”的评论

一楼翻译太奇芭了

估计是直接google翻译的。。。

我已经重翻了,这家伙估计来捣乱的

刚刚我也重新翻译了一遍,发上来才发现你已经翻译了,还翻译得更好=。=
晕死。。。哈哈

确实不错,顶一下。
我也新翻译了几段,不过只能保证语句通顺,技术上没有问题。

你也不错 哈哈

大家看这篇吧http://blog.jobbole.com/28506/ 来自stackoverflow上e-satis同学神一般的回复http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained

还有这篇http://blog.jobbole.com/21351/ 同样来自stackoverflow,http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python 大家看看收藏和评分就知道好坏了

好坏?
我提交的这篇文章来自Python-weekly推荐哦

这篇也不错~
renwofei423
renwofei423

引用来自“se77en.cc”的评论

引用来自“se77en.cc”的评论

引用来自“renwofei423”的评论

引用来自“LinuxQueen”的评论

引用来自“renwofei423”的评论

引用来自“crab2313”的评论

引用来自“renwofei423”的评论

引用来自“crab2313”的评论

一楼翻译太奇芭了

估计是直接google翻译的。。。

我已经重翻了,这家伙估计来捣乱的

刚刚我也重新翻译了一遍,发上来才发现你已经翻译了,还翻译得更好=。=
晕死。。。哈哈

确实不错,顶一下。
我也新翻译了几段,不过只能保证语句通顺,技术上没有问题。

你也不错 哈哈

大家看这篇吧http://blog.jobbole.com/28506/ 来自stackoverflow上e-satis同学神一般的回复http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained

还有这篇http://blog.jobbole.com/21351/ 同样来自stackoverflow,http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python 大家看看收藏和评分就知道好坏了

好坏?
我提交的这篇文章来自Python-weekly推荐哦
se77en
se77en

引用来自“se77en.cc”的评论

引用来自“renwofei423”的评论

引用来自“LinuxQueen”的评论

引用来自“renwofei423”的评论

引用来自“crab2313”的评论

引用来自“renwofei423”的评论

引用来自“crab2313”的评论

一楼翻译太奇芭了

估计是直接google翻译的。。。

我已经重翻了,这家伙估计来捣乱的

刚刚我也重新翻译了一遍,发上来才发现你已经翻译了,还翻译得更好=。=
晕死。。。哈哈

确实不错,顶一下。
我也新翻译了几段,不过只能保证语句通顺,技术上没有问题。

你也不错 哈哈

大家看这篇吧http://blog.jobbole.com/28506/ 来自stackoverflow上e-satis同学神一般的回复http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained

还有这篇http://blog.jobbole.com/21351/ 同样来自stackoverflow,http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python 大家看看收藏和评分就知道好坏了
se77en
se77en

引用来自“renwofei423”的评论

引用来自“LinuxQueen”的评论

引用来自“renwofei423”的评论

引用来自“crab2313”的评论

引用来自“renwofei423”的评论

引用来自“crab2313”的评论

一楼翻译太奇芭了

估计是直接google翻译的。。。

我已经重翻了,这家伙估计来捣乱的

刚刚我也重新翻译了一遍,发上来才发现你已经翻译了,还翻译得更好=。=
晕死。。。哈哈

确实不错,顶一下。
我也新翻译了几段,不过只能保证语句通顺,技术上没有问题。

你也不错 哈哈

大家看这篇吧http://blog.jobbole.com/28506/ 来自stackoverflow上e-satis同学神一般的回复http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained
renwofei423
renwofei423

引用来自“LinuxQueen”的评论

引用来自“renwofei423”的评论

引用来自“crab2313”的评论

引用来自“renwofei423”的评论

引用来自“crab2313”的评论

一楼翻译太奇芭了

估计是直接google翻译的。。。

我已经重翻了,这家伙估计来捣乱的

刚刚我也重新翻译了一遍,发上来才发现你已经翻译了,还翻译得更好=。=
晕死。。。哈哈

确实不错,顶一下。
我也新翻译了几段,不过只能保证语句通顺,技术上没有问题。

你也不错 哈哈
zicode
zicode

引用来自“renwofei423”的评论

引用来自“crab2313”的评论

引用来自“renwofei423”的评论

引用来自“crab2313”的评论

一楼翻译太奇芭了

估计是直接google翻译的。。。

我已经重翻了,这家伙估计来捣乱的

刚刚我也重新翻译了一遍,发上来才发现你已经翻译了,还翻译得更好=。=
晕死。。。哈哈

确实不错,顶一下。
我也新翻译了几段,不过只能保证语句通顺,技术上没有问题。
返回顶部
顶部
返回顶部
顶部