【问题标题】:What is the difference between these two solutions - lambda or loop - Python这两种解决方案有什么区别 - lambda 或循环 - Python
【发布时间】:2011-03-05 20:05:37
【问题描述】:

我想计算域内偶数的总和。我有两种解决方案,但我不确定每种解决方案的优缺点。哪个是最优解?

import sys
domain = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Cal1 = sum(filter(lambda n : n % 2 == 0, domain))
Cal2 = sum([n for n in domain if n % 2 == 0])
sys.stdout.write("Cal1 = {0}\n".format(Cal1))
sys.stdout.write("Cal2 = {0}\n".format(Cal2))

【问题讨论】:

    标签: python


    【解决方案1】:

    第二个真的应该只是一个生成器,而不是列表推导式(因为您实际上不需要创建一个列表来对生成器的输出求和):

    Cal2 = sum(n for n in domain if n % 2 == 0)
    

    这是完成此任务的现在首选(“pythonic”)方式。

    • 使用列表解析(包括[],您原来的Cal2)是不利的,因为它实际上构造了一个要返回的列表对象,这有开销。

    • 使用filter(您的Cal1)等同于生成器(没有-[] 版本),但需要更多的输入,并且读取不如仅使用生成器表达式(我上面贴的代码)。

    【讨论】:

    • thanx Dear ^_^ 那么,Lambda 有什么用? ..如何在我的程序中使用?例子请..
    • lambda 用于当您需要将短函数传递给某物时,该函数只是输入的基本转换。例如,Python 库中的一些 sort 函数允许您将函数传递给它们以比较两个项目,如果您想以非标准方式对列表进行排序 - 那么您可以使用类似 @987654330 @。另外,不客气。 :)
    • @Karamela:示例:reduce(lambda acc, (i, x): acc ^ i ^ x, enumerate(arr), 0)stackoverflow.com/questions/2605766/…
    【解决方案2】:

    以下是老式 Mac 笔记本电脑上各种版本的速度:

    $ py26 -mtimeit -s'domain = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]' 'sum(filter(lambda n : n % 2 == 0, domain))'
    100000 loops, best of 3: 4.41 usec per loop
    $ py26 -mtimeit -s'domain = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]' 'sum([n for n in domain if n % 2 == 0])'
    100000 loops, best of 3: 2.69 usec per loop
    $ py26 -mtimeit -s'domain = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]' 'sum(n for n in domain if n % 2 == 0)'
    100000 loops, best of 3: 2.86 usec per loop
    

    请注意,虽然 genexp 版本无疑更酷,但 listcomp 稍微快一些(可能不足以担心,除非此代码处于您正在努力的紧密内部循环中优化鼻涕;-)。像往常一样,基于lambda 的版本要慢得多,正如其他人所提到的——lambda 在 Python 中是一种“不良关系”:-(。((不是defined 函数会明显执行这里更好,要么))

    【讨论】:

    • 现在尝试使用 1000000 个数字的域,看看会发生什么。生成器有设置开销,但使用更少的内存和更少的复制。在 100k 和 1M 元素之间,生成器版本会变得更快。
    • @Andrew,取决于可用(和可寻址的;-)RAM 的数量——只要一切都适合可寻址和物理可用的 RAM,listcomp 会更快。正如你所说的一百万个数字(Q 的域乘以 100000),和往常一样的旧 macbook:genexp 178 毫秒,listcomp 154 毫秒——你为什么不测量而不是自夸?当然,在某些点,物理可用/可寻址的 RAM 会用完,listcomp 会严重减慢甚至失败,而 genexp 仍然可以工作 - 但在任何采用 WAY的体面的现代机器上> 超过一百万#s。
    • 顺便说一句,@Andrew,你说 listcomp 进行额外复制也是完全错误的——它只是将计算的项目放在不同的位置,而不是一遍又一遍地放在同一个位置——没有复制. listcomp` 和 genexp` 都差不多 O(N) (每个都扣除了一些固定开销,但当然完全摊销了;-),listcomp 的乘数稍微小一些,所以它的运行速度快了大约 10%(6% 和 15 % 在这两个实验中)——“除非代码处于紧密的内部循环中,否则不必担心”,正如我在 A 中所说的!
    • 嗯,listcomp 确实为所有这些指针使用了更多内存,并最终复制了它们。我在发布之前做了测量。 1M 条目,listcomp 176 毫秒,生成器 159 毫秒,在我的 MacBook Pro 上。原因可能是生成器将所有内容都保存在缓存中,而 listcomp 的中间列表不适合此 CPU 上的 4MB 处理器缓存。这是一个非常大的缓存,所以缓存较小的机器会受到更大的伤害。
    • @Andrew McGregor:在我的机器上,对于 Python 2.6.5 上的 1M 条目,geneexpr 比 listcomp 稍快(128 vs 120 ms)。用n & 1 替换n % 2 得到10493 毫秒。在 Python3 上,listcomp 比genexpr 稍快。在 ideone listcomp 上总是比genexpr ideone.com/DNGpm ideone.com/8vybt
    【解决方案3】:

    您的第二种方法是所谓的列表理解。列表推导式可用于实现您以前使用 filtermap 之前的语言介绍。请参阅this previous question,了解与您所要求的类似的关于列表推导与地图的讨论。

    正如Amber 所写,推荐的 Pythonic 方法是使用生成器。通过列表理解,您的整个过滤列表将被构建,然后求和。使用生成器,它会随着它的进行而被求和,而不会在内存中拥有完整的列表。当您处理超过 10 个项目时,这会产生更大的不同。

    【讨论】:

      【解决方案4】:

      +1 其他出色的答案。

      奖励:生成器表达式更快...

      $ python -m timeit -s 'L = xrange(10)' 'sum(filter(lambda n: n % 2 == 0, L))'
      100000 loops, best of 3: 3.59 usec per loop
      
      $ python -m timeit -s 'L = xrange(10)' 'sum(n for n in L if n % 2 == 0)'
      100000 loops, best of 3: 2.82 usec per loop
      

      Docs re timeit.

      【讨论】:

      • 啊哈..thanx,现在我明白了:D
      猜你喜欢
      • 2013-09-08
      • 2019-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-14
      相关资源
      最近更新 更多