以下是我的代码的简化示例。
>>> def action(num):
print "Number is", num
>>> items = [1, 3, 6]
>>> for i in [j for j in items if j > 4]:
action(i)
Number is 6
我的问题如下:不好的做法(出于代码清晰度等原因)只需用理解来替换for
循环,这仍然会调用{{1}功能?那就是:
action
答案 0 :(得分:4)
这根本不应该使用生成器或理解。
JQuery
发电机懒洋洋地评价。表达式def action(num):
print "Number is", num
items = [1, 3, 6]
for j in items:
if j > 4:
action(i)
仅将生成器表达式返回给调用者。除非你明确地用尽它,否则什么都不会发生。列表理解会急切地评估,但是,在这种特殊情况下,你会留下一个没有目的的(action(j) for j in items if j > 2)
。只需使用常规循环。
答案 1 :(得分:1)
虽然我个人赞成Tigerhawk's solution,但他和willywonkadailyblah的解决方案(现已删除)之间可能存在中间立场。
willywonkadailyblah的观点之一是:
为什么要创建新列表而不是仅使用旧列表?你已经有了过滤掉正确元素的条件,那么为什么要将它们放在内存中并为它们回来呢?
避免此问题的一种方法是使用过滤的延迟评估,即只有在使用for循环进行迭代时才进行过滤,方法是使生成器表达式的过滤部分而不是列表理解:
for i in (j for j in items if j > 4):
action(i)
<强>输出强>
Number is 6
老实说,我认为Tigerhawk的解决方案是最好的解决方案。这只是一种可能的选择。
我提出这个的原因是它让我想起了C#中的很多LINQ查询,你在这里定义了一种从一个语句(LINQ表达式)中的序列中提取,过滤和投影元素的惰性方法,然后可以使用与该查询分开的for each
循环,以对每个元素执行某些操作。
答案 2 :(得分:1)
这是不好的做法。首先,您的代码片段不会产生所需的输出。你会得到类似的东西:<generator object <genexpr> at 0x03D826F0>
。
其次,列表理解用于创建序列,而生成器用于创建对象流。通常,它们没有副作用。你的动作函数是副作用的一个主要例子 - 它打印输入并且什么都不返回。相反,生成器应该为它生成的每个项目,接受输入并计算一些输出。例如
doubled_odds = [x*2 for x in range(10) if x % 2 != 0]
通过使用生成器,您正在混淆代码的目的,即改变全局状态(打印某些东西),而不是创建对象流。 然而,仅使用for循环会使代码稍长(基本上只是更多的空格),但是您可以立即看到目的是将函数应用于选择的项目(而不是创建新的流/项目列表)。
for i in items:
if i < 4:
action(i)
请记住,生成器仍然是循环结构,并且底层字节码或多或少相同(如果有的话,生成器的效率稍差),和就会失去清晰度。生成器和列表推导很好,但对他们来说这不是正确的情况。