這個問題在這里已經有了答案: 在生成器中與 Python 3.3 中的產量一起回傳 2 個答案 35 分鐘前關閉。
為什么
yield [cand]
return
導致不同的輸出/行為
return [[cand]]
最小可行示例
- 使用遞回
- 使用的版本的輸出與使用的版本的
yield [1]; return輸出不同return [[1]]
def foo(i):
if i != 1:
yield [1]
return
yield from foo(i-1)
def bar(i):
if i != 1:
return [[1]]
yield from bar(i-1)
print(list(foo(1))) # [[1]]
print(list(bar(1))) # []
最小可行反例
- 不使用遞回
- 使用的版本的輸出與使用
yield [1]; return的版本的輸出相同return [[1]]
def foo():
yield [1]
return
def foofoo():
yield from foo()
def bar():
return [[1]]
def barbar():
yield from bar()
print(list(foofoo())) # [[1]]
print(list(barbar())) # [[1]]
完整的背景關系
我正在解決Leetcode #39: Combination Sum并且想知道為什么一種解決方案有效,而另一種則無效:
作業解決方案
from functools import cache # requires Python 3.9
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
@cache
def helper(targ, i=0):
if i == N or targ < (cand := candidates[i]):
return
if targ == cand:
yield [cand]
return
for comb in helper(targ - cand, i):
yield comb [cand]
yield from helper(targ, i 1)
N = len(candidates)
candidates.sort()
yield from helper(target)
非作業解決方案
from functools import cache # requires Python 3.9
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
@cache
def helper(targ, i=0):
if i == N or targ < (cand := candidates[i]):
return
if targ == cand:
return [[cand]]
for comb in helper(targ - cand, i):
yield comb [cand]
yield from helper(targ, i 1)
N = len(candidates)
candidates.sort()
yield from helper(target)
輸出
在以下輸入
candidates = [2,3,6,7]
target = 7
print(Solution().combinationSum(candidates, target))
作業解決方案正確列印
[[3,2,2],[7]]
而非作業解決方案列印
[]
我想知道為什么yield [cand]; return有效,但return [[cand]]沒有。
uj5u.com熱心網友回復:
在生成器函式中,return只需定義與隱式引發的例外關聯的值,StopIteration以指示迭代器已用盡。它不是在迭代期間產生的,并且大多數迭代構造(例如for回圈)有意忽略StopIteration例外(這意味著回圈結束,您不在乎是否有人將隨機垃圾附加到僅表示“我們完成”的訊息)。
例如,嘗試:
>>> def foo():
... yield 'onlyvalue' # Existence of yield keyword makes this a generator
... return 'returnvalue'
...
>>> f = foo() # Makes a generator object, stores it in f
>>> next(f) # Pull one value from generator
'onlyvalue'
>>> next(f) # There is no other yielded value, so this hits the return; iteration over
--------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
...
StopIteration: 'returnvalue'
正如你所看到的,你的return值在某種意義上確實得到了“回傳”(它沒有被完全丟棄),但它從來沒有被任何正常迭代的東西看到,所以它在很大程度上是無用的。除了將生成器用作協程的極少數情況(您正在使用生成.send()器.throw()的實體并使用 手動推進它next(genobj))之外,不會看到生成器的回傳值。
簡而言之,您必須選擇一個:
- 在函式中的
yield任何地方使用,它是一個生成器(無論特定呼叫的代碼路徑是否到達 ayield)并且只是結束生成(同時可能在例外return中隱藏一些資料)。StopIteration無論你做什么,呼叫生成器函式“回傳”一個新的生成器物件(你可以回圈直到用盡),它永遠不會回傳在生成器函式內部計算的原始值(它甚至不會開始運行,直到你回圈至少一次)。 - 不要使用
yield, 并按return預期作業(因為它不是生成器函式)。
作為解釋return正常回圈結構中的值發生了什么的示例,這for x in gen():有效地擴展為 C 優化版本:
__unnamed_iterator = iter(gen())
while True:
try:
x = next(__unnamed_iterator)
except StopIteration: # StopIteration caught here without inspecting it
break # Loop ends, StopIteration exception cleaned even from sys.exc_info() to avoid possible reference cycles
# body of loop goes here
# Outside of loop, there is no StopIteration object left
As you can see, the expanded form of the for loop has to look for a StopIteration to indicate the loop is over, but it doesn't use it. And for anything that's not a generator, the StopIteration never has any associated values; the for loop has no way to report them even if it did (it has to end the loop when it's told iteration is over, and the arguments to StopIteration are explicitly not part of the values iterated anyway). Anything else that consumes the generator (e.g. calling list on it) is doing roughly the same thing as the for loop, ignoring the StopIteration in the same way; nothing except code that specifically expects generators (as opposed to more generalized iterables and iterators) will ever bother to inspect the StopIteration object (at the C layer, there are optimizations that StopIteration objects aren't even produced by most iterators; they return NULL and leave the set exception empty, which all iterator protocol using things know is equivalent to returning NULL and setting a StopIteration object, so for anything but a generator, there isn't even an exception to inspect much of the time).
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/426293.html
