【问题标题】:In Python, is it better to use list comprehensions or for-each loops?在 Python 中,使用列表推导或 for-each 循环更好吗?
【发布时间】:2011-02-20 10:26:37
【问题描述】:

以下哪个更好用,为什么?

方法一:

for k, v in os.environ.items():
       print "%s=%s" % (k, v)

方法二:

print "\n".join(["%s=%s" % (k, v) 
   for k,v in os.environ.items()])

我倾向于将第一个引向更容易理解的方向,但这可能只是因为我是 Python 新手,而列表推导对我来说仍然有些陌生。第二种方式被认为更 Pythonic 吗?我假设没有性能差异,但我可能错了。这两种技术的优缺点是什么?

(代码取自Dive into Python

【问题讨论】:

  • 无论如何打印都很慢 - 追求可读性(for循环)。一般来说,首先考虑可读性。然后,当它看起来很慢时 - 配置文件和优化。除非您正在设计一个框架(库),否则您一开始可以摆脱臭代码。然后,您将了解哪些有效,哪些无效。

标签: python coding-style foreach list-comprehension


【解决方案1】:

我认为第一个,因为:

  • 它不会构建一个巨大的字符串。
  • 它不会构建一个庞大的列表(可以通过删除 [] 轻松地使用生成器修复)。

在这两种情况下,您都以相同的方式访问项目(使用字典迭代器)。

【讨论】:

    【解决方案2】:

    列表推导应该在 C 级别运行,所以如果有很大的循环,列表推导是不错的选择。

    【讨论】:

      【解决方案3】:

      我发现第一个示例更好 - 更少冗长、更清晰、更易读。

      毕竟,在我看来,请选择最能传达您意图的方式:

      程序应该为人而写 阅读,并且只是偶然地 执行的机器。

      --摘自 Abelson 和 Sussman 的“计算机程序的结构和解释”

      顺便说一句,既然你刚刚开始学习 Python,那就马上开始学习new String Formatting syntax

      for k, v in os.environ.items():
          print "{0}={1}".format(k, v)
      

      【讨论】:

      • 请注意 str.format() 仅存在于 python ver >= 2.6
      【解决方案4】:

      您选择的特定代码示例没有展示列表理解的任何优势,因为它被(错误地)用于打印的琐碎任务。在这个简单的例子中,我会选择简单的for 循环。

      在许多其他情况下,您可能希望为另一个函数或方法提供一个实际列表,而列表推导式是最简单、最易读的方法。

      可以通过将print 示例替换为涉及创建另一个实际列表的示例,通过在for 循环的每次迭代中附加一个示例来制作一个清楚地显示列表comp 优越性的示例:

      L = []
      for x in range(10):
          L.append(x**2)
      

      提供与以下相同的L

      L = [x**2 for x in range(10)]
      

      【讨论】:

      • 您的示例的问题在于它是微不足道的。为什么不实际使用问题中的代码来显示差异?可能有一些时间信息?
      • 另一个好处是您可以将结果列表提供给另一个函数或链/嵌套理解。
      【解决方案5】:

      如果迭代是为了它的副作用而进行的(就像在你的“打印”示例中一样),那么循环会更清晰。

      如果执行迭代是为了构建一个复合值,那么列表推导通常更具可读性。

      【讨论】:

      • +1:列表推导来自函数式编程,而 for 循环来自命令式编程。
      • 我同意可读性,但可读性不是现实世界代码的主要考虑因素。也许在演示代码中,可读性是王道。在现实世界中,性能有时是王道,尽管并非总是如此。生成器在使用时会被使用,因为它们会带来一些好处。 OP问“哪个更好”。哪一个以明显更好的方式完成工作(在某些情况下资源浪费更少,在某些情况下更快,根据您的要求)。
      • 我最初会追求可读性,除非有理由不这样做。在给出的示例中,没有理由尝试将打印速度加快几微秒。在更一般的情况下,如果您正在迭代表达式的副作用,那么产生副作用的语句很可能比您使用的迭代构造花费更多的时间。
      • 俗话说,过早优化是万恶之源。将其编写为可读的,这样,当您可以测试完成的程序并实际找出需要优化的地方时,您就会了解代码在做什么。
      • @badp:对 everything 使用列表推导会使 Haskell 等函数式语言非常难以阅读。
      【解决方案6】:

      列表理解的速度是显式循环的两倍多。基于 Ben James 的变体,但将 x**2 替换为更简单的 x+2 函数,两种选择是:

      def foo(n):
        L = []
        for x in xrange(n):
          L.append(x+2)
        return L
      
      
      def bar(n):
        return [x+2 for x in xrange(n)]
      

      计时结果:

      In [674]: timeit foo(1000)
      10000 loops, best of 3: 195 us per loop
      
      In [675]: timeit bar(1000)
      10000 loops, best of 3: 81.7 us per loop
      

      列表理解大获全胜。

      我同意可读性应该优先于性能优化。然而,可读性在旁观者的眼中。当我第一次学习 Python 时,列表理解是一件我很难理解的奇怪事情! :-O 但是一旦我习惯了它,它就变成了一个非常好的速记符号。如果你想精通 Python,你必须掌握列表理解。

      【讨论】:

      • 没错,使用列表推导式,您正在编写更多高性能代码,这些代码将被高级 pythonistas 理解,而不是被一年级学生理解。
      • 列表推导更好地表达了实际逻辑,接近于集合生成器符号。一般来说,for 循环必须“在头部执行”才能查看它的作用。
      • 我没有在实际的 Python 中尝试过,但是您的示例代码看起来好像可以通过增加 n 来任意提高性能:除非 Python 采取措施来抵消这种影响,否则 foo 使用 O( n^2) 算法,而 bar 在 O(n) 中运行。
      • foo 和 bar 一样是 O(n)。 Python 的 list.append 是 O(1)。
      【解决方案7】:

      我同意@Ben、@Tim、@Steven:

      • 可读性是最重要的(“导入”以提醒自己是什么)
      • listcomp 可能会或可能不会比迭代循环版本快得多...这取决于进行的函数调用的总数
      • 如果您确实决定使用包含大型数据集的 listcomps,最好使用 生成器表达式

      例子:

      print "\n".join("%s=%s" % (k, v) for k,v in os.environ.iteritems())
      

      在上面的代码 sn-p 中,我做了 两个 更改...我将 listcomp 替换为 genexp,并将方法调用更改为 iteritems()。 [这种趋势在 Python 3 中向前发展,iteritems() 替换并重命名为 items()。]

      【讨论】:

      • 为什么对每个人来说,可读性始终是最重要的事情?
      • 这是一种预付费用的心态,使这样的代码“Pythonic...”,对你的程序员同事友善和尊重,尤其是。因为您的代码在您的工作地点可能会比您更长寿。很难用语言解释清楚。再次查看“import this”……Python 之禅为您提供了与 Python 哲学相关的一般指导方针。
      • @Warren:因为可读性导致可维护性和可读性使性能改进变得容易得多。永远不要忘记关注分析器在优化时指向的那些部分。
      猜你喜欢
      • 1970-01-01
      • 2019-08-03
      • 2014-04-25
      • 2016-09-28
      • 1970-01-01
      • 2021-10-22
      • 2022-08-04
      • 2013-07-21
      • 2015-11-12
      相关资源
      最近更新 更多