我的代码中有以下行:
processed = [singleRun.postProcess() for singleRun in simObjects if singleRun._ran]
postProcess()
函数需要几秒钟才能按simObject
运行,在此期间,simObjects
的{{1}}个属性可能会变为._ran
属性True
_ran是simObjects类的@property,它可以在此脚本范围之外的操作中更改。
是postProcess()
运行,因为每个对象都是从simObjects列表迭代过来并传递if语句,或者是postProcess()
生成的列表和然后评估?
完全公开(我现在意识到这一点非常重要):脚本将计算作业提交给分布式计算系统并检查文件以查看它们何时完成(通过@property,._ran
),所以问题的原因是列表是先生成_ran
的,或者每个对象都是postProcess()
生成的,因为它在列表中被迭代(并且发现是{ {1}})。
答案 0 :(得分:3)
postProcess()
)的计算结果为True,则会调用 if singleRun._ran
一次。
您可以使用以下虚拟测试来测试列表压缩的行为:
def foo():
print 'foo'
test = [foo() for f in [1, 2, 3, 4, 5] if f % 2 == 0]
“foo”只打印两次,因为在列表中只有2个偶数。
或实例:
class Foo(object):
def m(self):
print 'm called'
objects = [Foo(), Foo(), Foo(), Foo(), Foo()]
x = [obj.m() for i, obj in enumerate(objects) if i % 2 == 0]
当你编写一个“伪三元运算符”(老实说,我不知道它是否在Python中有一个特殊名称)表达式时,Python会应用相同的惰性方法:
foo() if condition else bar()
在这种情况下,只有当条件为真时才调用foo(),尽管事实上它是第一个......如果没有必要,Python足够聪明以避免额外的计算;)
答案 1 :(得分:2)
postProcess()
。
您可以使用lambda基于singleRun._ran
更改该行为以首先创建列表,然后再次处理列表,运行过滤后的项目。
基本上你可以:
filtered = [singleRun.postProcess for singleRun in simObjects if singleRun._ran]
processed = [item() for item in filtered]
这将首先形成一个列表filtered
,不会有任何延迟,然后处理它,而不检查singleRun._ran
这是否是你想要做的。
答案 2 :(得分:2)
每次迭代都会执行决策,而不是预先分配为条件掩码数组。在list displays中,声明"新列表的元素是通过将每个for或if子句从左到右嵌套,并将表达式评估为每次到达最里面的块时都会产生一个列表元素。"
换句话说,你的代码:
processed = [singleRun.postProcess() for singleRun in simObjects if singleRun._ran]
在功能上类似于:
processed = []
for singleRun in simObjects:
if singleRun._ran:
processed.append(singleRun.postProcess())
这意味着,如果_ran
在postProcess
正在运行时更改为postProcess
,则该更改将导致另一个processed
调用。两种形式的代码之间的主要区别在于名称map
仅在第一个变体中完成后才绑定到列表(并且,与{{1}}一样,它可能会预先分配列表而不是调整大小它反复)。