我发现自己经常想写这样的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]
所以我有两个问题:
newList = [(x,a=f(x),b=g(a)) for x in bigList if a<p and b<q]
但这不起作用。是否有充分的理由不支持这种语法?能够 它可以通过像this之类的东西来完成吗? 或者我只需要使用多个listcomp或for-loop?
答案 0 :(得分:11)
我有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
还有一些数据:
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)
second-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)]
答案 1 :(得分: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]
扩展了更长版本以便更容易理解:
[(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]
这会尽可能少地调用f
和g
(每个f(x)
的值不会< p
永远不会调用g
和{{ 1}}只会为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
和a
使用生成器表达式,这样它们就不必实际实例化列表,只需在必要时进行评估。
答案 2 :(得分:4)
随着列表理解变得更加复杂,它们也开始变得非常难以阅读。在这种情况下,通常最好将内部转换为生成器函数,并为它们提供一个(希望)有意义的名称。
# 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))
答案 3 :(得分:3)
如果在表达式中调用两次函数(包括在列表推导中),它实际上会被调用两次。 Python无法知道您的函数是纯函数还是程序函数。它会在你告诉它时调用它,在这种情况下是两次。
无法在列表推导中分配变量,因为在Python中,赋值是一个语句,而不是表达式。
听起来你应该使用完整的循环,而不是列表理解。