假设我有一个函数,我想选择是否返回结果。这很容易编码:
def foo(N, is_return=False):
l = []
for i in range(N):
print(i)
if is_return:
l.append(i)
if is_return:
return l
Run Code Online (Sandbox Code Playgroud)
但是现在可以说我希望该函数成为一个生成器。我会写这样的东西:
def foo_gen(N, is_return=False):
for i in range(N):
print(i)
if is_return:
yield i
Run Code Online (Sandbox Code Playgroud)
所以大概 when is_returnis Falsethenfoo_gen只是一个没有返回值的函数, when is_returnisTrue foo_gen是一个生成器,我希望有两个不同的调用:
In [1]: list(foo_gen(3, is_return=True))
0
1
2
Out[2]: [0, 1, 2]
Run Code Online (Sandbox Code Playgroud)
因为当它是一个生成器并且您必须遍历产生的值时,并且:
>>> In [2]: foo_gen(3)
0
1
2
Run Code Online (Sandbox Code Playgroud)
因为当它不是生成器并且它只是具有副作用并且您不必遍历它时。但是,后一种行为不起作用,而只是返回生成器。你不能从中得到任何东西:
In [3]: list(foo_gen(3, is_return=False))
0
1
2
Out[3]: []
Run Code Online (Sandbox Code Playgroud)
但这并不是很好,并且对于那些不希望必须遍历任何内容以产生副作用的 API 用户来说是令人困惑的。
反正有没有In [2]在函数中做出行为?
为此,您需要包装foo_gen在另一个函数中,该函数返回生成器或对其本身进行迭代,如下所示:
def maybe_gen(N, is_return=False):
real_gen = foo_gen(N)
if is_return:
for item in real_gen:
pass
else:
return real_gen
def foo_gen(N):
for i in range(N):
print(i)
yield i
>>> list(maybe_gen(3))
0
1
2
[0, 1, 2]
>>> maybe_gen(3, is_return=True)
0
1
2
>>>
Run Code Online (Sandbox Code Playgroud)
原因是yield函数中任何地方的出现使它成为生成器函数。没有办法让函数在调用时决定它是否是生成器函数。相反,您必须有一个非生成器函数,它在运行时决定是返回生成器还是其他东西。
也就是说,这样做很可能不是一个好主意。你可以看到maybe_genwhen is_returnis True做什么是完全微不足道的。它只是迭代生成器而不做任何事情。这尤其愚蠢,因为在这种情况下,生成器本身除了打印之外什么都不做。
最好让函数 API 保持一致:要么总是返回一个生成器,要么永远不做。一个更好的主意是只有两个函数foo_gen,即生成器,print_gen或者无条件打印它的东西。如果你想要生成器,你可以调用foo_gen. 如果您只是想打印它,则print_gen改为调用,而不是将“标志”参数传递给foo_gen.
关于你最后的评论:
但这并不是很好,并且对于那些不希望必须遍历任何内容以产生副作用的 API 用户来说是令人困惑的。
如果 API 指定函数返回一个生成器,用户应该期望必须对其进行迭代。如果 API 说它不返回一个生成器,用户不应该期望必须对其进行迭代。API 应该只说一个或另一个,这将使用户清楚地知道期望什么。更令人困惑的是有一个笨拙的 API,它告诉用户他们必须传递一个标志来确定他们是否获得了生成器,因为这使用户的期望变得复杂。
| 归档时间: |
|
| 查看次数: |
331 次 |
| 最近记录: |