今天有一段python程序,简化后大致如下:
import random
def do_something():
print("do_something() is called")
some_list = [1, 123, 500, 2]
for item in some_list:
yield item
def foo():
r = random.random()
print(f"r = {r}")
if r > 0.1:
return do_something()
else:
yield 0
def bar():
results = foo()
print(f"Results type: {type(results)}")
for item in results:
print(item)
bar()
输出结果如下:
Results type: <class 'generator'>
r = 0.240407787079716
可以看出,do_something()根本没被调用。
究其原因,混合使用 return
和 yield
在同一个函数中确实会导致一些不直观的行为。在 foo()
函数中,当 r > 0.1
时,尽管看起来是通过 return
返回了 do_something()
的迭代器,但由于 foo
中存在 yield
语句,整个函数都被视为生成器函数。因此,foo()
的调用者会收到一个生成器对象(生成器的每个元素为生成器,而不是预想的数字),而不是直接接收到从 do_something()
返回的迭代器。这可能导致调用者在处理返回值时遇到一些意料之外的行为。
因此,正确的做法应该是让函数统一用return或yield来返回值,而不应该混用二者。
如果要统一使用yield,需要在调用方进行迭代,如下:
def foo():
r = random.random()
print(f"r = {r}")
if r > 0.1:
for item in do_something():
yield item
else:
yield 0
如果要统一使用return,将 yield
语句包装成类似 return
的形式来减少代码冗余是一个很好的想法。方法如下:
def yield_once(value):
yield value
def foo():
r = random.random()
print(f"r = {r}")
if r > 0.1:
return do_something()
else:
return yield_once(0)
上述两种修改方法都可以得到预想的结果:
r = 0.8093101114456676
Results type: <class 'generator'>
do_something() is called
1
123
500
2