代码之家  ›  专栏  ›  技术社区  ›  Alex. S.

“收益率”关键字的作用是什么?

  •  8943
  • Alex. S.  · 技术社区  · 16 年前

    这个词有什么用 yield 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_candidates 是否返回列表?单一元素?又叫了吗?后续通话何时停止?


    1.这段代码是由Jochen Schulz(jrschulz)编写的,他为度量空间创建了一个很棒的Python库。这是指向完整源的链接: Module mspace .
    40 回复  |  直到 3 年前
        1
  •  16394
  •   Neuron MonoThreaded    4 年前

    了解什么 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... “on是一个不可数的词; lists strings ,文件。。。

    生成器是迭代器,是一种iterable 您只能迭代一次 它们会动态生成值 :

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

    除了你用过的,它是一样的 () 而不是 [] 表演 for i in mygenerator 第二次,因为生成器只能使用一次:它们计算0,然后忘记它并计算1,然后逐个结束计算4。

    产量

    是一个关键字,用于 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
    

    掌握 ,你必须明白 调用函数时,在函数体中编写的代码不会运行。 函数只返回生成器对象,这有点棘手。

    然后,您的代码将从每次停止的地方继续 for 使用发电机。

    现在最难的部分是:

    第一次 对于 产量 ,然后它将返回循环的第一个值。然后,每个后续调用将运行您在函数中编写的循环的另一次迭代,并返回下一个值。这将一直持续到生成器被认为是空的,当函数运行时没有命中 . 这可能是因为循环已经结束,或者因为您不再满足 "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 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 candidate's 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不关心方法的参数是否是列表。Python需要iterables,因此它可以处理字符串、列表、元组和生成器!这就是所谓的duck类型,也是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
    ...
    

    对于Python3,使用 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__() 方法)。 Iterables是可以从中获取迭代器的任何对象。迭代器是允许您在iterables上进行迭代的对象。

    这篇文章中有更多关于它的内容 how for loops work

        2
  •  2260
  •   nzz user28409    5 年前

    理解的捷径 yield

    当你看到一个函数 语句,应用此简单技巧了解将发生什么:

    1. 插入一行 result = [] 在函数的开头。
    2. 更换每个 yield expr 具有 result.append(expr)
    3. 插入一行 return result
    4. 耶-没有了 产量 声明!阅读并理解代码。

    这个技巧可能会让您了解函数背后的逻辑,但实际发生了什么 产量

    首先是 迭代器协议 -当你写作时

    for x in mylist:
        ...loop body...
    

    Python执行以下两个步骤:

    1. mylist :

      呼叫 iter(mylist) next() __next__() 在Python 3中)。

      [这是大多数人忘记告诉你的步骤]

    2. 使用迭代器循环项目:

      继续打电话给警察 从步骤1返回的迭代器上的。来自的返回值 下一个() x 然后执行循环体。如果有例外 StopIteration 是从内部提出的 下一个()

    事实上,Python可以随时执行上述两个步骤 绕圈子 对象的内容-因此它可以是for循环,但也可以是类似代码的 otherlist.extend(mylist) (在哪里 otherlist 是一个Python列表)。

    迈利斯特 是一个 可迭代的 因为它实现了迭代器协议。在用户定义的类中,可以实现 __iter__() 迭代器 . 迭代器是具有 下一个() __国际热核聚变实验堆(iter) 下一个() 在同一个班级,并且有 __国际热核聚变实验堆(iter) self

    这就是迭代器协议,许多对象实现这个协议:

    1. 内置列表、字典、元组、集合、文件。
    2. 实现 __国际热核聚变实验堆(iter)
    3. 发电机。

    请注意 for 循环不知道它正在处理什么类型的对象——它只是遵循迭代器协议,并且很乐意在调用时一个接一个地获取项 下一个() . 内置列表逐个返回其项,字典返回 钥匙 线 产量 包括:

    def f123():
        yield 1
        yield 2
        yield 3
    
    for item in f123():
        print item
    

    而不是 产量 声明,如果你有三个 return 声明 f123() 只有第一个将被执行,函数将退出。但是 f123() 它叫什么 返回yield语句中的任何值!它返回一个生成器对象。此外,函数并没有真正退出——它进入了挂起状态。当 对于 产量 它以前从返回,执行下一行代码,在本例中为 语句,并将其作为下一项返回。这会一直发生,直到函数退出,此时生成器将引发 停止迭代 ,循环退出。

    因此generator对象有点像适配器——在一端,它通过公开 下一个() 保持一致性的方法 对于

    为什么使用发电机?

    通常,您可以编写不使用生成器但实现相同逻辑的代码。一个选择是使用我前面提到的临时列表“技巧”。这并不适用于所有情况,例如,如果您有无限循环,或者当您有一个很长的列表时,它可能会导致内存使用效率低下。另一种方法是实现一个新的iterable类SomethingItemer,它将状态保存在实例成员中,并在实例成员中执行下一个逻辑步骤 下一个() 在Python3)方法中。根据逻辑,代码在 下一个()

        3
  •  650
  •   Georgy rassa45    6 年前

    这样想:

    next() 方法所以一个屈服函数最终是这样的:

    原文:

    def some_function():
        for i in xrange(4):
            yield i
    
    for i in some_function():
        print i
    

    这基本上就是Python解释器对上述代码所做的:

    class it:
        def __init__(self):
            # Start at -1 so that we get 0 when we add 1 below.
            self.count = -1
    
        # The __iter__ method will be called once by the 'for' loop.
        # The rest of the magic happens on the object returned by this method.
        # In this case it is the object itself.
        def __iter__(self):
            return self
    
        # The next method will be called repeatedly by the 'for' loop
        # until it raises StopIteration.
        def next(self):
            self.count += 1
            if self.count < 4:
                return self.count
            else:
                # A StopIteration exception is raised
                # to signal that the iterator is done.
                # This is caught implicitly by the 'for' loop.
                raise StopIteration
    
    def some_func():
        return it()
    
    for i in some_func():
        print i
    

    为了更深入地了解幕后发生的事情 for

    iterator = some_func()
    try:
        while 1:
            print iterator.next()
    except StopIteration:
        pass
    

    这更有意义还是让你更困惑

        4
  •  534
  •   ninjagecko    3 年前

    这个 yield 关键字简化为两个简单事实:

    1. 如果编译器检测到 关键词 在任何地方 return 陈述 立即 返回一个 惰性“挂起列表”对象 叫发电机
    2. 发电机是可拆卸的。什么是 可迭代的 ? 这有点像一辆汽车 list set range 或者用一个 用于按特定顺序访问每个元素的内置协议 .

    生成器是一个延迟的、增量挂起的列表 语句允许您使用函数表示法对列表值进行编程 发电机应逐渐吐出。

    generator = myYieldingFunction(...)  # basically a list (but lazy)
    x = list(generator)  # evaluate every element into a list
    
       generator
           v
    [x[0], ..., ???]
    
             generator
                 v
    [x[0], x[1], ..., ???]
    
                   generator
                       v
    [x[0], x[1], x[2], ..., ???]
    
                           StopIteration exception
    [x[0], x[1], x[2]]     done
    

    基本上,只要 遇到语句时,函数暂停并保存其状态,然后根据python迭代器协议发出“列表中的下一个返回值”(到某些语法结构,如重复调用的for循环) next() 抓住了一个 StopIteration generator expressions ; 生成器函数更强大,因为您可以将参数传递回暂停的生成器函数,使用它们实现协同路由。稍后再谈。


    基本示例(“列表”)

    让我们定义一个函数 makeRange 这就像Python的一样 . 使命感 makeRange(n) 返回一个生成器:

    def makeRange(n):
        # return 0,1,2,...,n-1
        i = 0
        while i < n:
            yield i
            i += 1
    
    >>> makeRange(5)
    <generator object makeRange at 0x19e4aa0>
    

    要强制生成器立即返回其挂起的值,可以将其传递到 list() (就像你可以做任何事情一样):

    >>> list(makeRange(5))
    [0, 1, 2, 3, 4]
    

    上面的示例可以被认为只是创建了一个列表,您可以将其附加到该列表并返回:

    # return a list                  #  # return a generator
    def makeRange(n):                #  def makeRange(n):
        """return [0,1,2,...,n-1]""" #      """return 0,1,2,...,n-1"""
        TO_RETURN = []               # 
        i = 0                        #      i = 0
        while i < n:                 #      while i < n:
            TO_RETURN += [i]         #          yield i
            i += 1                   #          i += 1
        return TO_RETURN             # 
    
    >>> makeRange(5)
    [0, 1, 2, 3, 4]
    


    iterable是列表理解的最后一部分,所有生成器都是iterable的,因此它们通常是这样使用的:

    #                  < ITERABLE >
    >>> [x+10 for x in makeRange(5)]
    [10, 11, 12, 13, 14]
    

    为了更好地感受发电机,您可以使用 itertools 模块(请务必使用 chain.from_iterable 而不是 chain 如有保证)。例如,您甚至可以使用生成器实现无限长的惰性列表,如 itertools.count() . 你可以实现你自己的 def enumerate(iterable): zip(count(), iterable) 产量 while循环中的关键字。

    implementing coroutines 或者非确定性编程或者其他优雅的东西。然而,我在这里介绍的“懒惰列表”观点是您将发现的最常用的用法。


    幕后

    这就是“Python迭代协议”的工作原理。也就是说,当你这样做的时候会发生什么 list(makeRange(5)) . 这就是我在前面描述的“懒惰、增量列表”。

    >>> x=iter(range(5))
    >>> next(x)  # calls x.__next__(); x.next() is deprecated
    0
    >>> next(x)
    1
    >>> next(x)
    2
    >>> next(x)
    3
    >>> next(x)
    4
    >>> next(x)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    

    只是调用对象 .__next__() 函数,它是“迭代协议”的一部分,可以在所有迭代器上找到。您可以手动使用 下一个() 函数(和迭代协议的其他部分)来实现奇特的东西,通常以牺牲可读性为代价,所以尽量避免这样做。。。


    Coroutine 例子:

    def interactiveProcedure():
        userResponse = yield makeQuestionWebpage()
        print('user response:', userResponse)
        yield 'success'
    
    coroutine = interactiveProcedure()
    webFormData = next(coroutine)  # same as .send(None)
    userResponse = serveWebForm(webFormData)
    
    # ...at some point later on web form submit...
    
    successStatus = coroutine.send(userResponse)
    

    通常情况下,大多数人不会在意以下区别,可能想停止阅读这里的内容。

    可迭代的 任何“理解for循环概念”的对象都像列表吗 [1,2,3] 迭代器 [1,2,3].__iter__() . A. 发电机 与任何迭代器完全相同,只是编写方式不同(使用函数语法)。

    当您从列表中请求迭代器时,它将创建一个新的迭代器。但是,当您从迭代器请求迭代器时(您很少这样做),它只会给您一个自身的副本。

    因此,在不太可能的情况下,你没有做这样的事情。。。

    > x = myRange(5)
    > list(x)
    [0, 1, 2, 3, 4]
    > list(x)
    []
    

    ... 然后记住,生成器是一个 ; 也就是说,它是一次性使用的。如果你想重新使用它,你应该打电话 myRange(...) 再一次如果需要使用结果两次,请将结果转换为列表并存储在变量中 x = list(myRange(5)) . 那些绝对需要克隆生成器的人(例如,正在进行骇人听闻的黑客元编程的人)可以使用 itertools.tee ( still works in Python 3 )如果绝对必要,因为 copyable iterator Python PEP standards proposal 已经推迟了。

        5
  •  480
  •   Aaron Hall    4 年前

    yield Python中的关键字do?

    • 具有 yield ,当被呼叫时, Generator .
    • 生成器是迭代器,因为它们实现 iterator protocol ,以便可以对其进行迭代。
    • 也可以使用发电机 ,使其在概念上成为 协同程序 .
    • 代表 yield from .
    • (附录评论了几个答案,包括最上面的一个,并讨论了 return

    发电机:

    仅在函数定义中是合法的,并且 列入 产量 在函数定义中,使其返回生成器。

    生成器的思想来自其他语言(见脚注1),具有不同的实现。在Python的生成器中,代码的执行是 frozen

    产量 提供 简单的方法 implementing the iterator protocol ,由以下两种方法定义: __iter__ next __next__ (Python 3)。这两种方法 使一个对象成为一个迭代器,您可以使用它进行类型检查 Iterator 抽象基础 collections 单元

    >>> def func():
    ...     yield 'I am'
    ...     yield 'a generator!'
    ... 
    >>> type(func)                 # A function with yield is still a function
    <type 'function'>
    >>> gen = func()
    >>> type(gen)                  # but it returns a generator
    <type 'generator'>
    >>> hasattr(gen, '__iter__')   # that's an iterable
    True
    >>> hasattr(gen, 'next')       # and with .next (.__next__ in Python 3)
    True                           # implements the iterator protocol.
    

    生成器类型是迭代器的子类型:

    >>> import collections, types
    >>> issubclass(types.GeneratorType, collections.Iterator)
    True
    

    >>> isinstance(gen, types.GeneratorType)
    True
    >>> isinstance(gen, collections.Iterator)
    True
    

    特征 is that once exhausted

    >>> list(gen)
    ['I am', 'a generator!']
    >>> list(gen)
    []
    

    如果你想再次使用它的功能,你必须再做一次(见脚注2):

    >>> list(func())
    ['I am', 'a generator!']
    

    def func(an_iterable):
        for item in an_iterable:
            yield item
    

    上面的简单生成器也相当于下面的生成器—对于Python 3.3(在Python 2中不可用),您可以使用 yield from

    def func(an_iterable):
        yield from an_iterable
    

    然而 还允许委托给子生成器, 这将在下一节关于与子公司的合作委托的部分中解释。

    产量 形成一个表达式,允许将数据发送到生成器(参见脚注3)

    下面是一个例子,请注意 received

    def bank_account(deposited, interest_rate):
        while True:
            calculated_interest = interest_rate * deposited 
            received = yield calculated_interest
            if received:
                deposited += received
    
    
    >>> my_account = bank_account(1000, .05)
    

    首先,我们必须使用内置函数将生成器排队, next 下一个 __下一个__ 您正在使用的Python:

    >>> first_year_interest = next(my_account)
    >>> first_year_interest
    50.0
    

    现在我们可以将数据发送到发生器中。( Sending None is the same as calling next .) :

    >>> next_year_interest = my_account.send(first_year_interest + 1000)
    >>> next_year_interest
    102.5
    

    屈服于

    现在,回想一下 屈服于

    
    def money_manager(expected_rate):
        # must receive deposited value from .send():
        under_management = yield                   # yield None to start.
        while True:
            try:
                additional_investment = yield expected_rate * under_management 
                if additional_investment:
                    under_management += additional_investment
            except GeneratorExit:
                '''TODO: write function to send unclaimed funds to state'''
                raise
            finally:
                '''TODO: write function to mail tax info to client'''
            
    
    def investment_account(deposited, manager):
        '''very simple model of an investment account that delegates to a manager'''
        # must queue up manager:
        next(manager)      # <- same as manager.send(None)
        # This is where we send the initial deposit to the manager:
        manager.send(deposited)
        try:
            yield from manager
        except GeneratorExit:
            return manager.close()  # delegate?
    

    现在,我们可以将功能委托给子生成器,并且可以使用它 通过发电机,如上所述:

    my_manager = money_manager(.06)
    my_account = investment_account(1000, my_manager)
    first_year_return = next(my_account) # -> 60.0
    

    next_year_return = my_account.send(first_year_return + 1000)
    next_year_return # 123.6
    

    您可以阅读更多关于 在里面 PEP 380.

    其他方法:关闭和抛出

    这个 close GeneratorExit 死刑被冻结。这也将被称为 __del__ 那么你呢

    my_account.close()
    

    您还可以抛出可以在生成器中处理的异常 或传播回用户:

    import sys
    try:
        raise ValueError
    except:
        my_manager.throw(*sys.exc_info())
    

    提出:

    Traceback (most recent call last):
      File "<stdin>", line 4, in <module>
      File "<stdin>", line 6, in money_manager
      File "<stdin>", line 2, in <module>
    ValueError
    

    结论

    我相信我已经涵盖了以下问题的各个方面:

    这是什么意思 Python中的关键字do?

    原来 这方面的详尽例子。如果你想要更多或者有一些建设性的批评,请通过评论让我知道


    附件:

    对顶级/公认答案的批评**

    • 可迭代的 ,仅以列表为例。请参阅我上面的参考资料,但概括地说:iterable具有 __iter__ 方法返回 . 一 .next (Python 2或 .__next__ (Python 3)方法,该方法由 for 循环直到它上升 StopIteration 一旦它这样做了,它将继续这样做。
    • 然后,它使用生成器表达式来描述生成器是什么。因为生成器只是创建 迭代器 ,这只会使事情变得混乱,而我们还没有达成共识 产量 部分
    • 在里面 控制发电机耗尽 下一个 方法,当他应该使用内置函数时, 下一个
    • 产量 真的。
    • 没有讨论使用的方法 产量 屈服于 在Python3中。

    对答案建议的批评 在表达或理解中。

    expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
                         ('=' (yield_expr|testlist_star_expr))*)
    ...
    yield_expr: 'yield' [yield_arg]
    yield_arg: 'from' test | testlist
    

    由于yield是一个表达式,一些人吹嘘在理解或生成器表达式中使用它很有趣——尽管没有引用特别好的用例。

    discussing deprecating its allowance . 以下是邮件列表中的相关帖子:

    2017年1月30日19:05,Brett Cannon写道:

    2017年1月29日星期日16:39,克雷格·罗德里格斯写道:

    这两种方法我都同意。让事情保持Python 3中的状态

    我认为这是一个SyntaxError,因为你没有从中得到你所期望的 语法。

    可维护的。

    就实现目标而言,我们可能需要:

    • 3.7中的SyntaxWarning或DeprecationWarning
    • 2.7.x中的Py3k警告
    • 3.8中的SyntaxError

    干杯,尼克。

    此外,还有一个问题 outstanding issue (10544) 这似乎指向了这个方向 从不

    底线是,直到CPython的开发人员告诉我们其他情况: 不要放 产量 在表达或理解中。

    回来 生成器中的语句

    Python 2 :

    在生成器函数中 语句不允许包含 expression_list 回来 指示发电机已完成并将导致

    表达式列表 基本上是由逗号分隔的任意数量的表达式-基本上,在Python 2中,您可以使用 ,但不能返回值。

    在里面 Python 3

    在生成器函数中 回来 语句表示生成器已完成并将导致 停止迭代 被抚养。返回值(如果有)用作构造的参数 并成为 StopIteration.value

    脚注

    1. 提案中引用了CLU、SATER和Icon语言 向Python介绍生成器的概念。总的想法是 用户需要的数据点。这本来是应该的 superior in performance to other approaches, including Python threading

    2. 例如,这意味着 range 对象不是 s、 即使它们是可移植的,因为它们可以重复使用。像列表一样,他们的 方法返回迭代器对象。

    最初是作为声明介绍的,意思是 只能出现在代码块中的行首。 现在 产量 创建屈服表达式。 https://docs.python.org/2/reference/simple_stmts.html#grammar-token-yield_stmt 这一变化是 proposed 你可能会收到它。要发送数据,必须能够将其分配给某个对象,并且 对于这一点,一个声明是行不通的。

        6
  •  384
  •   Fang    6 年前

    yield 就像 return -它返回您告诉它的任何内容(作为生成器)。不同之处在于,下次调用生成器时,执行从最后一次调用 产量 陈述与回归不同, 当发生屈服时,堆栈帧不会被清除,但是控制权会被转移回调用方,因此下次调用函数时,其状态将恢复。

    对于您的代码,函数 get_child_candidates

    list.extend 调用迭代器,直到它耗尽为止。在您发布的代码示例中,只返回一个元组并将其附加到列表中会更清楚。

        7
  •  279
  •   Claudiu    12 年前

    还有一件事要提:一个函数实际上不需要终止。我编写了如下代码:

    def fib():
        last, cur = 0, 1
        while True: 
            yield cur
            last, cur = cur, last + cur
    

    然后我可以在其他类似的代码中使用它:

    for f in fib():
        if some_condition: break
        coolfuncs(f);
    

    它确实有助于简化一些问题,并使一些事情更容易处理。

        8
  •  271
  •   Oren    5 年前

    对于那些喜欢一个简单的工作示例的人,可以考虑一下这个交互式Python会话:

    >>> def f():
    ...   yield 1
    ...   yield 2
    ...   yield 3
    ... 
    >>> g = f()
    >>> for i in g:
    ...   print(i)
    ... 
    1
    2
    3
    >>> for i in g:
    ...   print(i)
    ... 
    >>> # Note that this time nothing was printed
    
        9
  •  248
  •   Bob Stein    6 年前

    TL;博士

    与此相反:

    def square_list(n):
        the_list = []                         # Replace
        for x in range(n):
            y = x * x
            the_list.append(y)                # these
        return the_list                       # lines
    

    这样做:

    def square_yield(n):
        for x in range(n):
            y = x * x
            yield y                           # with this one.
    

    每当你发现自己从头开始建立一个列表, yield

    这是我第一次屈服的“啊哈”时刻。


    产量 sugary

    建立一系列的东西

    >>> for square in square_list(4):
    ...     print(square)
    ...
    0
    1
    4
    9
    >>> for square in square_yield(4):
    ...     print(square)
    ...
    0
    1
    4
    9
    

    不同的行为:

    单程 :您只能迭代一次。当一个函数有收益率时,我们称之为 generator function . 和 iterator 这就是它的回报。这些术语很有启发性。我们失去了容器的便利性,但获得了根据需要计算的级数的幂,并且任意长。

    懒惰的 ,它推迟了计算。有屈服点的函数 当你调用它时,它实际上根本不会执行。 iterator object 这还记得它从哪里停下来的。每次你打电话 next() 在迭代器上(这发生在for循环中),执行会向前推进几英寸,直到下一个成品。 return

    多才多艺的 . 数据不必全部存储在一起,可以一次存储一个数据。它可以是无限的。

    >>> def squares_all_of_them():
    ...     x = 0
    ...     while True:
    ...         yield x * x
    ...         x += 1
    ...
    >>> squares = squares_all_of_them()
    >>> for _ in range(4):
    ...     print(next(squares))
    ...
    0
    1
    4
    9
    

    多次传球 这个系列不会太长,只要打电话就行了 list() 关于它:

    >>> list(square_yield(4))
    [0, 1, 4, 9]
    

    这个词选得很好 产量 因为 both meanings

    产量 -生产或提供(如农业)

    …提供系列中的下一个数据。

    产量 -让路或放弃(如政治权力)

    …在迭代器前进之前放弃CPU执行。

        10
  •  219
  •   Andreas    6 年前

    def get_odd_numbers(i):
        return range(1, i, 2)
    def yield_odd_numbers(i):
        for x in range(1, i, 2):
           yield x
    foo = get_odd_numbers(10)
    bar = yield_odd_numbers(10)
    foo
    [1, 3, 5, 7, 9]
    bar
    <generator object yield_odd_numbers at 0x1029c6f50>
    bar.next()
    1
    bar.next()
    3
    bar.next()
    5
    

    如你所见,在第一种情况下 foo 一次将整个列表保存在内存中。对于一个包含5个元素的列表来说没什么大不了的,但是如果你想要一个500万元素的列表呢?这不仅是一个巨大的内存消耗器,而且在调用函数时构建它也要花费大量时间。

    在第二种情况下, bar 给你一个发电机。生成器是可移植的,这意味着您可以在 for 循环等,但每个值只能访问一次。所有值也不会同时存储在内存中;generator对象“记住”上次调用它时它在循环中的位置——这样,如果使用iterable来(比如)计数到500亿,则不必一次全部计数到500亿,并存储500亿个数字进行计数。

        11
  •  215
  •   Jon Skeet    6 年前

    它正在返回一台发电机。我对Python不是特别熟悉,但我相信它与 C#'s iterator blocks 如果你熟悉这些。

    好像生成器方法已暂停 . 现在很明显,你不能真正“暂停”一个方法,所以编译器会为你建立一个状态机,让你记住你现在在哪里,以及局部变量是什么样子。这比自己编写迭代器容易得多。

        12
  •  193
  •   Peter Mortensen icecrime    7 年前

    在描述如何使用发电机的众多伟大答案中,有一种我认为尚未给出的答案。以下是编程语言理论的答案:

    这个 yield 延续

    编程语言理论中的连续体是一种更基本的计算,但它们并不经常被使用,因为它们极难推理,也很难实现。但什么是延拓的概念很简单:它是一个尚未完成的计算状态。在此状态下,将保存变量的当前值、尚未执行的操作等。然后,在程序中稍后的某个时刻,可以调用continuation,这样程序的变量将重置为该状态,并执行保存的操作。

    这种更一般形式的延续可以通过两种方式实现。在 call/cc

    在continuation-passing样式(CPS)中,continuation只是程序员显式管理并传递给子例程的普通函数(仅在函数为第一类的语言中)。在这种风格中,程序状态由闭包(以及恰好编码在闭包中的变量)表示,而不是由驻留在堆栈上某处的变量表示。管理控制流的函数接受continuation作为参数(在CPS的一些变体中,函数可以接受多个continuation),并通过调用控制流来操纵控制流,方法是简单地调用它们,然后返回。延续传递样式的一个非常简单的示例如下:

    def save_file(filename):
      def write_file_continuation():
        write_stuff_to_file(filename)
    
      check_if_file_exists_and_user_wants_to_overwrite(write_file_continuation)
    

    在这个(非常简单的)示例中,程序员将实际写入文件的操作保存到一个continuation中(可能是一个非常复杂的操作,需要写出许多细节),然后将该continuation(即,作为一级闭包)传递给另一个进行更多处理的操作符,然后在必要时给它打电话。(我在实际的GUI编程中经常使用这种设计模式,因为它可以节省代码行,更重要的是,可以在GUI事件触发后管理控制流。)


    现在让我们讨论Python中的生成器。生成器是延续的特定子类型。鉴于 continuations通常能够保存 计算 (即程序的调用堆栈), . 尽管如此,这个定义对于生成器的某些用例来说有点误导。例如:

    def f():
      while True:
        yield 4
    

    这显然是一个合理的iterable,其行为定义良好——每次生成器对其进行迭代时,它都返回4(并且永远如此)。但是,在考虑迭代器时,可能不会想到iterable的原型类型(即。, for x in collection: do_something(x)

    重申:Continuations可以保存程序堆栈的状态,生成器可以保存迭代的状态。这意味着连续体比生成器更强大,但生成器也更容易。语言设计者更容易实现它们,程序员也更容易使用它们(如果您有时间,请尝试阅读和理解) this page about continuations and call/cc ).

    但您可以轻松地将生成器实现(并概念化)为一种简单、特定的延续传递样式:

    无论何时 产量 调用时,它告诉函数返回一个延续。再次调用该函数时,它将从停止的位置开始。因此,在伪伪码(即,非伪码,但非代码)中,生成器的 next 方法基本如下:

    class Generator():
      def __init__(self,iterable,generatorfun):
        self.next_continuation = lambda:generatorfun(iterable)
    
      def next(self):
        value, next_continuation = self.next_continuation()
        self.next_continuation = next_continuation
        return value
    

    产量 关键字实际上是真正生成器函数的语法糖,基本上类似于:

    def generatorfun(iterable):
      if len(iterable) == 0:
        raise StopIteration
      else:
        return (iterable[0], lambda:generatorfun(iterable[1:]))
    

    产量

        13
  •  176
  •   Peter Mortensen icecrime    7 年前

    我想对数字序列进行运算,但我不想为序列的创建而烦恼,我只想专注于我想做的运算。因此,我做了以下工作:

    • 我打电话给你,告诉你我想要一个以特定方式产生的数字序列,然后我告诉你算法是什么。
      此步骤对应于 def yield .
    • 稍后,我会告诉你,“好的,准备好告诉我数字的顺序”。
      注意,你还没有告诉我任何数字;你只要抓起你的纸和铅笔。
    • 我问你,“告诉我下一个号码”,你告诉我第一个号码;之后,你等我问你下一个电话号码。你的工作是记住你在哪里,你已经说过的数字,以及下一个数字是什么。我不在乎细节。
      .next() 在生成器对象上。
    • 重复上一步,直到
    • 最终,你可能会走到尽头。你不告诉我一个数字;你只要喊一声,“别急!我完了!不要再打电话了!”
      此步骤对应于生成器对象结束其作业并引发 StopIteration 例外 return .

    产量 ); 它开始执行,每次执行命令时都会暂停 产量 值,它从上一个点开始继续。它完全符合Python的迭代器协议的设计,该协议描述了如何顺序请求值。

    迭代器协议最著名的用户是 for Python中的命令。所以,每当你做一个

    for item in sequence:
    

    如果 sequence 对象 如上所述;结果是一样的:你一个接一个地从序列中读取项目。

    def 产量 关键字不是创建生成器的唯一方法;这只是创建一个的最简单方法。

    有关更准确的信息,请阅读 iterator types 这个 yield statement generators 在Python文档中。

        14
  •  155
  •   Mike McKerns    11 年前

    虽然很多答案说明了为什么你会使用 yield 要创建生成器,可以使用 产量 . 创建一个协同程序非常容易,它支持在两个代码块之间传递信息。我不会重复任何已经给出的关于使用 创建一个生成器。

    帮助了解 产量 在下面的代码中,您可以使用手指在具有 . 每次你的手指碰到地板的时候 产量 next 或者 send 待输入。当 调用时,您将跟踪代码,直到找到 产量 产量 计算并返回给调用方,然后等待。什么时候 下一个 如果再次调用,则通过代码执行另一个循环。但是,您会注意到,在协同程序中, 产量 邮寄 进入 屈服函数。如果 邮寄 那么 接收发送的值,并将其从左侧吐出,然后跟踪代码,直到达到 产量 下一个 被称为)。

    >>> 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()
    
        15
  •  151
  •   Community CDub    4 年前

    还有一个 yield 用法和含义(从Python 3.3开始):

    yield from <expr>
    

    从…起 PEP 380 -- Syntax for Delegating to a Subgenerator

    提出了一种语法,用于将生成器的部分操作委托给另一个生成器。这允许将包含“屈服”的代码段分解出来并放入另一个生成器中。此外,允许子生成器返回一个值,并且该值可供委托生成器使用。

    此外 this 将介绍(从Python 3.5开始):

    async def new_coroutine(data):
       ...
       await blocking_action()
    

    避免将协同程序与常规生成器混淆(今天) 产量

        16
  •  138
  •   Wizard    7 年前

    所有的答案都很好,但是对新手来说有点难。

    return 陈述

    yield 他们是双胞胎。 回来

    1. 回来 .
    def num_list(n):
        for i in range(n):
            return i
    

    In [5]: num_list(3)
    Out[5]: 0
    

    回来 永远不要让你快乐地获胜,只要执行一次就放弃。

    1. 来了 产量

    代替 回来 具有 产量

    In [10]: def num_list(n):
        ...:     for i in range(n):
        ...:         yield i
        ...:
    
    In [11]: num_list(3)
    Out[11]: <generator object num_list at 0x10327c990>
    
    In [12]: list(num_list(3))
    Out[12]: [0, 1, 2]
    

    现在,你赢得了所有的数字。

    相比 回来 产量 运行您计划的时间。 回来 return one of them 产量 return all of them . 这叫做 iterable

    1. 还有一步我们可以重写 产量 回来
    In [15]: def num_list(n):
        ...:     result = []
        ...:     for i in range(n):
        ...:         result.append(i)
        ...:     return result
    
    In [16]: num_list(3)
    Out[16]: [0, 1, 2]
    

    它的核心是 产量

    列表之间的区别 回来

    您将始终从列表对象中获取[0,1,2],但只能从“对象”中检索它们 产量 输出“一次”。所以,它有了一个新名字 generator 中显示的对象 Out[11]: <generator object num_list at 0x10327c990>

    总之,作为对它的隐喻:

    • 回来
    • list 发电机 你是双胞胎吗
        17
  •  126
  •   alinsoar    4 年前

    从编程的角度来看,迭代器的实现如下 thunks .

    为了实现迭代器、生成器和线程池,以实现并发执行等功能,可以使用 messages sent to a closure object ,它有一个调度器,而 dispatcher answers to "messages" .

    " next " 是发送给关闭的消息,由“ “打电话。

    有很多方法可以实现这种计算。我使用了变异,但是可以通过返回当前值和下一个值(使其 referential transparent ).Racket在一些中间语言中使用初始程序的一系列转换,其中一种重写使yield运算符在某些语言中使用更简单的运算符进行转换。

    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
    ->
    
        18
  •  121
  •   Dustin Getz sunsations    7 年前

    下面是一些Python示例,说明如何实际实现生成器,就好像Python没有为它们提供语法糖一样:

    作为Python生成器:

    from itertools import islice
    
    def fib_gen():
        a, b = 1, 1
        while True:
            yield a
            a, b = b, a + b
    
    assert [1, 1, 2, 3, 5] == list(islice(fib_gen(), 5))
    

    使用词汇闭包而不是生成器

    def ftake(fnext, last):
        return [fnext() for _ in xrange(last)]
    
    def fib_gen2():
        #funky scope due to python2.x workaround
        #for python 3.x use nonlocal
        def _():
            _.a, _.b = _.b, _.a + _.b
            return _.a
        _.a, _.b = 0, 1
        return _
    
    assert [1,1,2,3,5] == ftake(fib_gen2(), 5)
    

    使用对象闭包而不是生成器 ClosuresAndObjectsAreEquivalent )

    class fib_gen3:
        def __init__(self):
            self.a, self.b = 1, 1
    
        def __call__(self):
            r = self.a
            self.a, self.b = self.b, self.a + self.b
            return r
    
    assert [1,1,2,3,5] == ftake(fib_gen3(), 5)
    
        19
  •  111
  •   johnzachary    12 年前

    另外,请注意 yield 可以在协同程序中用作其在生成器函数中的双重用途。虽然它与代码片段的用途不同, (yield) 可以用作函数中的表达式。当调用方使用 send() (收益率)

    生成器和协同程序是设置数据流类型应用程序的一种很酷的方式。我认为了解这个词的其他用法是值得的 产量 函数中的语句。

        20
  •  96
  •   Peter Mortensen icecrime    7 年前

    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
    

    我不是Python开发人员,但在我看来 yield 保持程序流的位置,下一个循环从“屈服”位置开始。它似乎在那个位置等待,就在那个位置之前,返回一个外部值,下一次继续工作。

    这似乎是一个有趣而不错的能力:D

        21
  •  80
  •   Evgeni Sergeev    8 年前

    这是一个什么样的心理图像 yield

    当调用普通函数时,它将其局部变量放在堆栈上,进行一些计算,然后清除堆栈并返回。它的局部变量的值再也看不到了。

    用一个 产量 函数,当其代码开始运行时(即调用函数后,返回生成器对象,其 next() 方法),它类似地将其局部变量放入堆栈并计算一段时间。但是,当它击中目标时 语句,在清除堆栈中的部分并返回之前,它获取其局部变量的快照并将其存储在生成器对象中。它还会在代码中写下当前所处的位置(即特定位置) 产量 声明)。

    所以这是一种冻结函数,生成器依赖于它。

    下一个()

    比较以下示例:

    def normalFunction():
        return
        if False:
            pass
    
    def yielderFunction():
        return
        if False:
            yield 12
    

    当我们调用第二个函数时,它的行为与第一个函数非常不同。这个 产量 语句可能无法访问,但如果它出现在任何地方,它就会改变我们正在处理的内容的性质。

    >>> yielderFunction()
    <generator object yielderFunction at 0x07742D28>
    

    yielderFunction() 不运行它的代码,但用代码生成生成器。(也许把这些东西命名为 yielder 前缀用于可读性。)

    >>> gen = yielderFunction()
    >>> dir(gen)
    ['__class__',
     ...
     '__iter__',    #Returns gen itself, to make it work uniformly with containers
     ...            #when given to a for loop. (Containers return an iterator instead.)
     'close',
     'gi_code',
     'gi_frame',
     'gi_running',
     'next',        #The method that runs the function's body.
     'send',
     'throw']
    

    gi_code gi_frame 字段是存储冻结状态的位置。探索他们与 dir(..)

        22
  •  72
  •   ZF007    5 年前

    一个易于理解的示例: yield

    def f123():
        for _ in range(4):
            yield 1
            yield 2
    
    
    for i in f123():
        print (i)
    

    输出为:

    1 2 1 2 1 2 1 2
    
        23
  •  71
  •   Peter Mortensen icecrime    7 年前

    正如每个答案所暗示的那样, yield 用于创建序列生成器。它用于动态生成一些序列。例如,在网络上逐行读取文件时,可以使用 功能如下:

    def getNextLines():
       while con.isOpen():
           yield con.read()
    

    您可以在代码中使用它,如下所示:

    for line in getNextLines():
        doSomeThing(line)
    

    执行控制转移

    执行控制将从getNextLines()传输到 for

    因此,简而言之,一个具有以下代码的函数

    def simpleYield():
        yield "first time"
        yield "second time"
        yield "third time"
        yield "Now some useful value {}".format(12)
    
    for i in simpleYield():
        print i
    

    将打印

    "first time"
    "second time"
    "third time"
    "Now some useful value 12"
    
        24
  •  68
  •   smwikipedia    6 年前

    (我下面的回答仅从使用Python生成器的角度出发,而不是从 underlying implementation of generator mechanism ,这涉及到堆栈和堆操作的一些技巧。)

    什么时候 yield return 在python函数中,该函数被转换为一种特殊的函数,称为 generator function . 该函数将返回一个 generator 类型 产量 正常函数将在返回某个值后终止。但是在编译器的帮助下,生成器函数 可以想到 作为可恢复的。也就是说,将恢复执行上下文,并从上次运行开始继续执行。直到显式调用return,这将引发 StopIteration 发电机 one functional programming perspective 是最容易消化的。

    发电机 ,以及 iterator 根据我自己的理解。我希望这能帮助你抓住问题的关键 本质动机 迭代器和生成器的设计。这种概念也出现在其他语言中,如C#)

    据我所知,当我们想要处理一组数据时,我们通常首先将数据存储在某个地方,然后逐个处理。但是这个 缺乏经验的 因此,与其存储 data 本身直接,为什么不存储某种 metadata 间接地,即。 the logic how the data is computed .

    1. 在OO方法中,我们包装元数据 as a class . 这就是所谓的 迭代器 __next__() __iter__() 方法)。这也是常见的 iterator design pattern
    2. 在函数方法中,我们包装元数据 as a function . 这是 所谓 生成函数 . 但在引擎盖下,他回来了 generator object IS-A 迭代器,因为它还实现迭代器协议。

    无论哪种方式,都会创建一个迭代器,即可以为您提供所需数据的某个对象。OO方法可能有点复杂。不管怎样,用哪一个取决于你。

        25
  •  67
  •   Peter Mortensen icecrime    7 年前

    yield generator 它围绕着原始函数的主体。当 发电机 产量 然后暂停执行并计算为传递给的值 产量 . 它在每次迭代中重复此过程,直到执行路径退出函数。例如,

    def simple_generator():
        yield 'one'
        yield 'two'
        yield 'three'
    
    for i in simple_generator():
        print i
    

    简单输出

    one
    two
    three
    

    电源来自于将生成器与计算序列的循环一起使用,生成器每次执行循环停止以“生成”下一个计算结果,这样它可以动态计算列表,好处是为特别大的计算节省了内存

    说你想创建一个你自己的 range 函数生成一个可数范围,你可以这样做,

    def myRangeNaive(i):
        n = 0
        range = []
        while n < i:
            range.append(n)
            n = n + 1
        return range
    

    像这样使用它;

    for i in myRangeNaive(10):
        print i
    

    • 创建一个只使用一次的数组(这会浪费内存)
    • 这段代码实际上在该数组上循环了两次!:(

    幸运的是,Guido和他的团队非常慷慨地开发了发电机,所以我们可以这样做;

    def myRangeSmart(i):
        n = 0
        while n < i:
           yield n
           n = n + 1
        return
    
    for i in myRangeSmart(10):
        print i
    

    现在,在每次迭代中,生成器上的一个函数调用 next() 下一个() 执行到yield语句和yield'n',在下一次调用时,它将执行increment语句,跳回'while',对其求值,如果为true,它将停止并再次产生'n',它将以这种方式继续,直到while条件返回false,并且生成器跳转到函数的末尾。

        26
  •  63
  •   Peter Mortensen icecrime    7 年前

    产量是一个目标

    A. return

    如果你愿意 返回大量值的函数 使用 yield .

    更重要的是,, 产量

    就像CUDA语言中的barrier一样,它在获得控制权之前不会转移控制权 完整的。

    产量 . 然后,itll返回循环的第一个值。

        27
  •  61
  •   Peter Mortensen icecrime    7 年前

    许多人使用 return yield ,但在某些情况下 可以更高效、更易于使用。

    这里有一个例子 产量 绝对是最好的:

    回来 (功能上)

    import random
    
    def return_dates():
        dates = [] # With 'return' you need to create a list then return it
        for i in range(5):
            date = random.choice(["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th"])
            dates.append(date)
        return dates
    

    产量 (功能上)

    def yield_dates():
        for i in range(5):
            date = random.choice(["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th"])
            yield date # 'yield' makes a generator automatically which works
                       # in a similar way. This is much more efficient.
    

    dates_list = return_dates()
    print(dates_list)
    for i in dates_list:
        print(i)
    
    dates_generator = yield_dates()
    print(dates_generator)
    for i in dates_generator:
        print(i)
    

    这两个函数做相同的事情,但是 使用三行而不是五行,并且少了一个变量。

    这是代码的结果:

    Output

    正如您所看到的,这两个函数做相同的事情。唯一的区别是 return_dates() 给出一个列表和 yield_dates() 给一个发电机。

    现实生活中的一个例子可能是逐行读取文件,或者只是想制作一个生成器。

        28
  •  51
  •   Rafael    3 年前

    想象一下,你创造了一台非凡的机器,每天能产生成千上万的灯泡。机器将这些灯泡放在具有唯一序列号的盒子中。您没有足够的空间同时存储所有这些灯泡,因此您希望对其进行调整以按需生成灯泡。

    Python生成器与这个概念没有太大区别。假设您有一个名为 barcode_generator 为箱子生成唯一的序列号。显然,受硬件(RAM)限制,函数可以返回大量这样的条形码。一个更明智、更节省空间的选择是按需生成这些序列号。

    机器代码:

    def barcode_generator():
        serial_number = 10000  # Initial barcode
        while True:
            yield serial_number
            serial_number += 1
    
    
    barcode = barcode_generator()
    while True:
        number_of_lightbulbs_to_generate = int(input("How many lightbulbs to generate? "))
        barcodes = [next(barcode) for _ in range(number_of_lightbulbs_to_generate)]
        print(barcodes)
    
        # function_to_create_the_next_batch_of_lightbulbs(barcodes)
    
        produce_more = input("Produce more? [Y/n]: ")
        if produce_more == "n":
            break
    

    注意 next(barcode) 一点

    如您所见,我们有一个自包含的–函数–每次生成下一个唯一的序列号。此函数返回一个 发电机 ! 如您所见,我们不是每次需要新序列号时都调用函数,而是使用 next()

    惰性迭代器

    更准确地说,这台发电机是 惰性迭代器 ! 迭代器是帮助我们遍历一系列对象的对象。它叫 懒惰的 因为在需要之前,它不会将序列中的所有项加载到内存中。使用 next 在前面的示例中是 从迭代器中获取下一项的方法。这个 含蓄的 该方法用于循环:

    for barcode in barcode_generator():
        print(barcode)
    

    换句话说,一台发电机 看起来像 表现得像 迭代器。

    最后,现实世界的应用程序?在处理大序列时,它们通常很有用。想象一下读一本书 巨大的 来自磁盘的文件,包含数十亿条记录。在处理其内容之前,在内存中读取整个文件可能是不可行的(即,内存不足)。

        29
  •  50
  •   Will    10 年前

    yield 类似于函数的返回元素。不同的是 产量 list(generator()) .

        30
  •  50
  •   ballade4op52 Okezie    9 年前

    yield 关键字只收集返回的结果。想想 产量 return +=