我可以在Python列表推导中使用别名来防止它们被多次评估吗?

kra*_*lot 16 python alias list-comprehension

我发现自己经常想写这样的Python列表理解:

nearbyPoints = [(n, delta(n,x)) for n in allPoints if delta(n,x)<=radius]
Run Code Online (Sandbox Code Playgroud)

这有希望给出一些关于我为什么要这样做的背景,但是也有一些情况需要为每个元素计算/比较多个值:

newlist = [(x,f(x),g(f(x))) for x in bigList if f(x)<p and g(f(x))<q]
Run Code Online (Sandbox Code Playgroud)

所以我有两个问题:

  1. 是否会多次评估所有这些函数还是缓存结果?语言是指定还是特定于实现?我现在使用2.6,但3.x会不同吗?
  2. 有没有更简洁的方式来写它?有时f和g是长表达式,重复是容易出错并且看起来很混乱.我真的希望能够写下这个:
newList = [(x,a=f(x),b=g(a)) for x in bigList if a<p and b<q]
Run Code Online (Sandbox Code Playgroud)

但这不起作用.是否有充分的理由不支持这种语法?可以通过它有点像做这个?或者我只需要使用多个listcomp或for循环?

Her*_*ert 12

我有一个hack在list/dict comprehensions中创建别名.你可以使用这个for alias_name in [alias_value]技巧.例如,你有这个昂贵的功能:

def expensive_function(x):
    print("called the very expensive function, that will be $2")
    return x*x + x
Run Code Online (Sandbox Code Playgroud)

还有一些数据:

data = [4, 7, 3, 7, 2, 3, 4, 7, 3, 1, 1 ,1]
Run Code Online (Sandbox Code Playgroud)

然后你想在每个元素上应用昂贵的函数,并根据它进行过滤.你做的是:

result = [
    (x, expensive)
    for x in data
    for expensive in [expensive_function(x)] #alias
    if expensive > 3
]

print(result)
Run Code Online (Sandbox Code Playgroud)

第二个for只会遍历大小为1的列表,从而有效地使其成为别名.输出将显示昂贵的函数被调用12次,每个数据元素只调用一次.然而,函数的结果(最多)使用两次,一次用于滤波器,一次用于输出.

请始终确保使用像我这样的多行来布局这样的理解,并将#alias附加到别名所在的行.如果你使用别名,那么理解就会变得非常复杂,你应该帮助未来的代码读者获得你正在做的事情.这不是perl,你知道;).

为了完整性,输出:

called the very expensive function, that will be $2
called the very expensive function, that will be $2
called the very expensive function, that will be $2
called the very expensive function, that will be $2
called the very expensive function, that will be $2
called the very expensive function, that will be $2
called the very expensive function, that will be $2
called the very expensive function, that will be $2
called the very expensive function, that will be $2
called the very expensive function, that will be $2
called the very expensive function, that will be $2
called the very expensive function, that will be $2
[(4, 20), (7, 56), (3, 12), (7, 56), (2, 6), (3, 12), (4, 20), (7, 56), (3, 12)]
Run Code Online (Sandbox Code Playgroud)

代码:http://ideone.com/7mUQUt


Amb*_*ber 10

关于#1,是的,它们将被多次评估.

关于#2,这样做的方法是计算和过滤单独的理解:

简明版:

[(x,fx,gx) for (x,fx,gx) in ((x,fx,g(fx)) for (x,fx) in ((x,f(x)) for x in bigList) if fx < p) if gx<q]
Run Code Online (Sandbox Code Playgroud)

更长的版本扩展到更容易遵循:

[(x,f,g) for (x,f,g) in
  ((x,f,g(f)) for (x,f) in
     ((x,f(x)) for x in bigList)
  if f < p)
if g<q]
Run Code Online (Sandbox Code Playgroud)

这将调用fg尽可能少的时间尽可能(每个值f(x)是不是< p永远不会调用g,并且f将只在每个值调用一次bigList).

如果您愿意,还可以使用中间变量获得更整洁的代码:

a = ( (x,f(x)) for x in bigList )
b = ( (x,fx,g(fx)) for (x,fx) in a if fx<p )
results = [ c for c in b if c[2] < q ] # faster than writing out full tuples
Run Code Online (Sandbox Code Playgroud)

ab使用生成器表达式,以便它们不必实际实例化列表,并在必要时进行简单评估.