评估列表理解性能后对效率的困惑

时间:2014-01-13 00:08:51

标签: python list-comprehension

我今天对列表推导和filter()函数进行了一些实验,因为我有兴趣看看如果使用一个而不是另一个,是否会显着提高效率。 结果有点令人困惑。当我对偶数进行过滤时,列表推导的性能优于传统的嵌套结构,filter()函数优于~1.5x(即速度提高约1.5倍)。 但是,当我使用函数检查数字是否为素数时,filter()函数突然变得最快。

我在下面发布了更多详细信息,如果您想亲自试用,我会将代码上传到github:https://github.com/rasbt/list_comprehension_test

我多次测试了具有不同范围最大值n的代码,以确保结果一致且不受我机器上某些临时后台进程的影响。

我的问题:

  • 任何想法为什么过滤偶数时过滤功能如此慢?可能是因为lambda函数还是因为我将生成器对象转换为列表?
  • 为什么is_prime函数的结果如此相似,为什么过滤函数在这里最快?

第一部分:收集偶数


a)循环和else-if

even_nums = []
for i in range(1, n):
    if i % 2 == 0:
        even_nums.append(i)

b)列表理解:

even = [i for i in range(1, n) if i % 2 == 0]

c)filter()函数

even_nums = list(filter((lambda x: x%2 != 0), range(1, n)))
is_even的

结果

  • loop和else-if:1x(reference)
  • 列表理解:快1.5倍
  • filter()函数:快0.9倍

第二部分:收集素数


def is_prime(num):
    """ Returns True if input integer is a prime number. """
    prime = True
    if num < 2:
        prime = False

    elif num == 2:
        prime = True
    else:
        for i in range(2, num):
            if num % i == 0:
                prime = False    
                break
    return prime

a)循环和else-if

primes = []
for i in range(1, n):
    if is_prime(i):
        primes.append(i)

b)列表理解:

primes = [i for i in range(1, n) if is_prime(i)]

c)filter()函数

primes = list(filter(is_prime, range(1, n)))
is_prime的

结果

  • loop和else-if:1x(reference)
  • 列表理解:快0.98倍
  • filter()函数:快了1.13倍

1 个答案:

答案 0 :(得分:1)

如果以这种方式实施第一次测试,结果应该与第二次测试一致:

is_even = lambda i: i % 2 == 0
even = [i for i in range(1, n) if is_even(i)]

filter实现中,每次迭代都有一次函数调用(lambda),这是一个额外的步骤。第二个测试中不存在这种差异,因为在这种情况下,每次迭代时,两个实现都已经包含一次调用(is_prime)。

至于为什么filter略快一些,我怀疑它与filter是本机而不是python代码有关。考虑到列表推导仍然对每次迭代的python代码进行额外评估:即i之前的forfilter中不需要此评估步骤,这可能直接产生本机实现中的值。