Python中的列表推导,如何?

2024-05-17 05:06:08 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在读Python,我想做一个关于列表理解的问题。问题很简单:

Write a Program that gives the sum of the multiples of 3 and 5 before some n 

 take n = 1000 (Euler project, 1st problem)

我想这样做:

^{pr2}$

只有一行。。。但这行不通。在

1-这可以通过列表理解来实现?怎样??在

2-还有,什么时候使用列表理解是好的?在

谢谢!在


Tags: andoftheproject列表thatsomeprogram
3条回答

你快到了!试试这个:

mysum = sum([i for i in range(2,10) if i%2==0 or i%5==0])

这将在“循环”之外创建一个列表,然后将该列表传递给sum函数。在

mylist = [*some expression using i* for i in iterable]这样的列表理解是

^{pr2}$

mylist = [*some expression using i* for i in iterable if *boolean with i*]这样的列表理解是

mylist = []
for i in iterable:
    if *boolean with i*:
        mylist.append(*some expression using i*)

当您需要使用某个表达式构造一个新的列表时,您可以使用它们。列表理解实际上比等价的for循环更有效,因为它们在底层的C中执行代码,而不是通过解释的python。在

列表理解的要点是生成一个结果值列表,每个源值一个(或者每个匹配源值的一个,如果您有if子句)。在

换言之,它与map(或者是map和{}调用链,如果有多个子句的话),只是可以将每个新值描述为旧值的表达式,而不必将其包装在函数中。在

你不能把一个语句(比如mysum = mysum + i)放入理解中,而只能放在表达式中。而且,即使你能想出一个有你想要的副作用的表达,那仍然是一个令人困惑的误用理解。如果不需要结果值的列表,则不要使用列表理解。在

如果您只是尝试在循环中执行计算,请编写一个显式的for循环。在


如果您真的需要它是一行,您可以随时这样做:

for i in [i for i in range(2, 10) if i%2==0 or i%5==0]: mysum += i

构建一个清单,用一个理解循环;在for循环中计算副作用y。在

(当然,这是假设您已经在mysum中添加了一些值,例如,使用mysum = 0。)

而且,一般来说,当你想要一个仅仅循环一次的理解时,你想要的理解是一个生成器表达式,而不是一个列表理解。所以,把这些方括号变成括号,你会得到:

^{pr2}$

不过,不管怎样,这两行文字更具可读性和Python风格:

for i in (i for i in range(2, 10) if i%2==0 or i%5==0):
    mysum += i

…甚至三个:

not2or5 = (i for i in range(2, 10) if i%2==0 or i%5==0) 
for i in not2or5:
    mysum += i

如果您的语言使reduce/fold比循环更直观,那么Python有一个reduce函数。然而,仅仅使用它来消除for循环并将块语句转换为一个行程序,通常不被认为是Pythonic。在

更一般地说,在Python中,试图把东西塞进一行通常会使内容的可读性降低,而不是更多,这通常意味着您需要键入更多的字符和要处理的标记,这远远抵消了节省行所带来的任何收益。在


当然,在这个特定的例子中,您真正想做的就是将一个列表的值求和。而这正是sum所做的。这很难理解。所以:

mysum += sum(i for i in range(2, 10) if i%2==0 or i%5==0)

(同样,这是假设您已经在mysum中有要添加的内容。如果不是,只需将+=改为=。后面的例子也是如此,所以我不再解释了。)


尽管如此,我可能会将其写为显式嵌套块:

for i in range(2, 10):
    if i%2==0 or i%5==0:
        mysum += i

…或作为迭代器转换的序列(在本例中实际上只是一个转换):

not2or5 = (i for i in range(2, 10) if i%2==0 or i%5==0)
mysum += sum(not2to5)

以这种方式拆分代码是没有代价的(只要使用生成器表达式而不是列表理解),而且它通常会使代码的意图更加明显。在


有关生成器表达式的进一步解释:

生成器表达式与列表理解类似,只是它构建迭代器而不是列表。在某些函数式语言中只能使用“一次迭代”。(通常,这不是问题。在上面所有的例子中,我们唯一要做的就是将它传递给sum函数或在for循环中使用它,然后我们就再也不会引用它了。)当您迭代它时,每个值都是按需构造的,然后在到达下一个值之前释放。在

这意味着空间复杂度是恒定的,而不是线性的。你一次只能在内存中有一个值,而在一个列表中,你显然已经得到了所有的值。这通常是一个巨大的胜利。在

然而,时间复杂度不变。列表理解预先完成了所有的工作,所以它是线性时间来构建,然后免费使用。生成器表达式在您迭代它时执行工作,因此它是free构建,然后线性使用。不管怎样,同一时间。(实际上,由于缓存/内存局部性、流水线等原因,生成器表达式实际上可以快得多,更不用说避免了所有内存移动和分配开销。另一方面,对于一般情况,它的速度较慢,至少在CPython中是这样,因为它必须通过完整的迭代器协议,而不是列表的快速特殊大小写。)

(我在这里假设每一步的功是常数,显然[sum(range(i)) for i in range(n)]是n的二次方的,不是线性的…)

下面是我的两行实现,包括filter、sum和reduce:

def f(x): return x%3 == 0 or x%5 == 0
print sum(filter(f,range(2,1000)))

不错吧?你能给我解释一下这个代码:

^{pr2}$

相关问题 更多 >