2011-01-30 58 views
10

我发现自己常常想编写Python列表理解是这样的:别名表情

nearbyPoints = [(n, delta(n,x)) for n in allPoints if delta(n,x)<=radius] 

有希望能看到一些上下文,为什么我会想这样做,但有 也病例多个值需要计算/每 元件相比:

newlist = [(x,f(x),g(f(x))) for x in bigList if f(x)<p and g(f(x))<q] 

所以我有两个问题:

  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] 

,但不起作用。有没有支持这种语法的好理由? 可以通过类似this的方式完成吗? 或者我只需要使用多个listcomps或for循环?

回答

8

关于#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] 

加长版扩大,使之更容易遵循:

[(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] 

这将调用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 

ab使用生成器表达式,使他们没有实际实例列表,并简单地评价在必要时。

+0

+1虽然我有关于索引元组疑虑。而且,在某些情况下,函数或生成器函数是最佳选择。 – Apalala 2011-01-30 15:50:37

2
  1. 如果你在一个表达式(包括列表理解)中调用一个函数两次,它会真的被调用两次。 Python无法知道你的函数是纯函数还是程序函数。当你告诉它,在这种情况下,它会调用它两次。

  2. 无法在列表理解中分配变量,因为在Python中,赋值是语句而不是表达式。

这听起来像你应该使用一个完整的循环,而不是一个列表理解。

+1

我不想在理解中写出一个陈述,只是为了避免再输入一些语法糖而很高兴。也许类似“a:= f(x)”会更好。但正如你所指出的那样,由于函数是第二次评估,所以这并不会有太大的帮助。 – krashalot 2011-01-30 00:59:59

3

随着列表理解变得越来越复杂,他们也开始变得非常难以阅读。在这种情况下,将内部函数转换为生成函数并给它们一个(希望)有意义的名称通常会更好。

# First example 
def getNearbyPoints(x, radius, points): 
    """Yields points where 'delta(x, point) <= radius'""" 
    for p in points: 
     distance = delta(p, x) 
     if distance <= radius: 
      yield p, distance 

nearbyPoints = list(getNearbyPoints(x, radius, allPoints)) 


# Second example 
def xfg(data, p, q): 
    """Yield 3-tuples of x, f(x), g(f(x))""" 
    for x in data: 
     f = f(x) 
     if f < p: 
      g = g(f) 
      if g < q: 
       yield x, f, g 

newList = list(xfg(bigList, p, q)) 
8

我有一个hack在list/dict理解中创建别名。你可以使用for alias_name in [alias_value]技巧。比如你有这样的昂贵的功能:

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

和一些数据:

data = [4, 7, 3, 7, 2, 3, 4, 7, 3, 1, 1 ,1] 

然后你要在每个元素的运用昂贵的功能,并在此基础上还进行筛选。你要做的是:

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

print(result) 

第二个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)] 

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