Python 列表理解中的函数-是否经过多次评估
在python中,哪一种是更好的列表理解方法(在计算时间和cpu周期方面)。 在示例(1)中,值f(r)是在每次迭代中计算的,还是只计算一次并缓存Python 列表理解中的函数-是否经过多次评估,python,list-comprehension,Python,List Comprehension,在python中,哪一种是更好的列表理解方法(在计算时间和cpu周期方面)。 在示例(1)中,值f(r)是在每次迭代中计算的,还是只计算一次并缓存 y=[x*f(r)表示xlist中的x] c=f(r) y=[x*c代表xlist中的x] 在哪里 我可能会选择后者,因为Python编译器不知道函数是否有副作用,所以会为每个元素调用它。函数f将为每个元素调用。它会为每个迭代求值。看看这个: >>> def f(): ... print("func") ... >&
y=[x*f(r)表示xlist中的x]
c=f(r)
y=[x*c代表xlist中的x]
我可能会选择后者,因为Python编译器不知道函数是否有副作用,所以会为每个元素调用它。函数
f
将为每个元素调用。它会为每个迭代求值。看看这个:
>>> def f():
... print("func")
...
>>> [f() for i in range(4)]
func
func
func
func
[None, None, None, None]
正如您所说,如果f()没有副作用,那么将返回值存储在变量上并使用该变量将是一个更快的解决方案。对于编译器/解释器来说,确定函数不需要多次调用是非常复杂的。然后很可能会多次调用该函数。因此,使用第二种解决方案始终是最好的解决方案。与名称查找相比,函数的执行时间非常短,如果函数被多次调用并且每次都需要相同的值,则缓存该值是可以接受的。Python可能可以自由地执行一次或多次,我不确定我是否会依赖任何观察到的行为。它可能在下一个版本中更改
如果确保函数只调用一次对您很重要,请您自己调用并保存结果。以下是一个简单的方法:
>>> def f():
... print "called"
... return 1
...
>>> [1+f() for x in xrange(5)]
called
called
called
called
called
[2, 2, 2, 2, 2]
因此,是的,如果函数每次都是相同的,那么最好在列表理解之外调用它一次。如何只调用一次?除非解释器能够保证
f
没有副作用,否则它必须在每次迭代中调用它no?@Davy8,当然有办法保证函数没有副作用。如果Python今天没有实现任何功能,那么它将来仍然可以自由地实现。如果函数使用eval()
则无法确定任何内容。因此,每次通过循环对函数进行求值时,没有其他选择。是的,未来的版本可能会破坏语义,但是eval()
的想法太基本了,不能破坏。@S.Lott:我不认为它做了这么复杂的事情。但从原则上讲,这是完全有可能的,所以这个问题并不像你想象的那么愚蠢。@s.Lott,当分析一个函数的副作用时,有三种可能的结果:是的,不是的,也说不出来。你似乎认为第三个的存在排除了其他两个,但事实并非如此。只要将“是/不能说”的情况集中在一起,优化将是安全的。使用某些JIT技术分析代码甚至可能是免费的。“选择是否取决于f(r)求值的复杂程度?”在这里如何定义“复杂”?更重要的是,Python何时以及如何确定“复杂性”。而且,更重要的是,当f(r)
涉及eval()
时,如何确定任何事情?抱歉,我的意思不清楚,我的意思是“我应该”,因为用户应该根据函数复杂度在(1)和(2)之间选择,还是我应该总是选择(2)。@ensnap:?“值f(r)是否计算一次并缓存?”在第二个示例中,是。在第一个示例中,no.和Python(假设存在eval()
)无法为您进行优化。您在问什么?请更新问题以使其更清楚。@ensnap:“是否应按用户选择的“I”和“是否对函数进行多次计算”完全不同。请修改标题或问题以更好地匹配彼此。这非常令人困惑。@s.Lott标题问“函数是否在列表理解中被评估多次”。回答是肯定的。因此(2)是一个更好的方法。“可能”?考虑到函数可以使用eval()
,还有什么选择呢?我说可能是因为我没有写任何代码来证实我的理论。Python怎么可能“知道函数是否有副作用”?函数可以使用eval()
或exec
做任何事情。因为eval()
存在,函数必须每次求值,否则语义会严重受损。@S Lott为什么eval的存在会保证Python解释器无法确保没有副作用?没有apply函数或map函数。map正在创建生成器对象。我只是想为avo添加一种变通方法id python列表理解中的多个函数调用:将函数调用放在只计算一次的嵌套理解中。在您的示例中,我的意思是:[1+f_2;for f_2;in[f()]for x in xrange(5)]
——注意这里只调用一次f()
。
>>> def f():
... print "called"
... return 1
...
>>> [1+f() for x in xrange(5)]
called
called
called
called
called
[2, 2, 2, 2, 2]