【问题标题】:Use case for nested/multiple list comprehensions or generator expressions. When is it more elegant?嵌套/多个列表推导或生成器表达式的用例。什么时候更优雅?
【发布时间】:2010-10-13 12:24:10
【问题描述】:

我有时会看到这样的事情:

(k for k in (j for j in (i for i in xrange(10))))

现在这真的让我脑筋急转弯,我宁愿它不以这种方式呈现。

有没有使用这些嵌套表达式比嵌套循环更优雅、更易读的用例或示例?

编辑:感谢您提供简化方法的示例。这实际上不是我要求的,我想知道它是否有优雅的时候。

【问题讨论】:

  • “有时”?真的吗?你在读什么?
  • 代码 sn-ps,开源项目。这将得到我曾经管理过的任何东西的代码审查。
  • 你在哪里见过这样的东西?你能提供一个具体的网址吗?

标签: python list-comprehension generator-expression


【解决方案1】:

检查PEP 202,这是将列表推导语法引入语言的地方。

为了理解你的例子,Guido 自己有一个简单的规则:

  • 形式 [... for x... for y...] 嵌套,最后一个索引 变化最快,就像嵌套的 for 循环一样。

也来自 PEP 202,用于回答您的问题:

基本原理 列表推导式提供了一种更简洁的方式来创建列表 map() 和 filter() 和/或嵌套循环的情况 目前正在使用。

如果你有这样的情况,你会发现它更优雅。但是,恕我直言,在您的代码中,多个嵌套列表推导可能不如嵌套 for 循环那么清晰,因为 for 循环很容易在视觉上解析。

【讨论】:

    【解决方案2】:

    如果你担心一行太复杂,你可以拆分它:

    (k for k in 
        (j for j in 
            (i for i in xrange(10))))
    

    我总是发现在 Python 中的续行看起来有点奇怪,但这确实可以更容易地查看每个行的循环内容。由于额外的分配/查找不会造成或破坏任何东西,您也可以这样写:

    gen1 = (i for i in xrange(10))
    gen2 = (j for j in gen1)
    gen3 = (k for k in gen2)
    

    在实践中,我认为我从来没有嵌套过超过 2 层的理解,那时它仍然很容易理解。

    【讨论】:

      【解决方案3】:

      在你的例子中,我可能会写成:

      foos = (i for i in xrange(10))
      bars = (j for j in foos)
      bazs = (k for k in bars)
      

      鉴于更具描述性的名称,我认为这可能会很清楚,而且我无法想象会有任何可衡量的性能差异。

      也许您正在考虑更多的表达方式,例如:

      (x for x in xs for xs in ys for ys in lst)
      

      -- 实际上,这甚至是无效的。您必须按其他顺序排列:

      (x for ys in lst for xs in ys for x in xs)
      

      我可能会这样写,作为一种扁平化列表的快速方法,但总的来说,我认为您是这样写的:通过减少输入所节省的时间通常与您花费在正确生成生成器表达式上的额外时间相平衡。

      【讨论】:

        【解决方案4】:

        由于它们是生成器表达式,您可以将每个表达式绑定到它自己的名称,以使其更具可读性,而不会改变性能。将其更改为嵌套循环可能会损害性能。

        irange = (i for i in xrange(10))
        jrange = (j for j in irange)
        krange = (k for k in jrange)
        

        你选择哪个并不重要,我认为多行示例通常更具可读性。

        【讨论】:

          【解决方案5】:

          警告:优雅部分是品味问题。

          列表推导永远不会比相应的扩展 for 循环更清晰。 For 循环也比列表推导更强大。那么为什么要使用它们呢?

          什么是列表推导式简洁——它们允许你在一行中做某事。

          使用列表推导式的时机是当您需要某个列表时,它可以相当容易地动态创建,并且您不希望或不需要中间对象。当您需要将当前范围内的一些对象打包成一个可以输入函数的对象时,可能会发生这种情况,如下所示:

          list1 = ['foo', 'bar']
          list2 = ['-ness', '-ity']
          return filterRealWords([str1+str2 for str1 in list1 for str2 in list2])
          

          此代码的可读性与扩展版本差不多,但要短得多。它避免了创建/命名在当前范围内只使用一次的对象,这可以说是更优雅。

          【讨论】:

            【解决方案6】:

            表达式:

            (k for k in (j for j in (i for i in xrange(10))))
            

            相当于:

            (i for i in xrange(10))
            

            几乎是一样的:

            xrange(10)
            

            最后一个变体比第一个更优雅。

            【讨论】:

            • 我很确定这只是演示嵌套理解的 OP 含义,而不是实际示例
            【解决方案7】:

            我发现它在你有这样的代码的情况下非常有用和优雅:

            output = []
            for item in list:
                if item >= 1:
                    new = item += 1
                    output.append(new)
            

            你可以像这样使它成为一个单行:

            output = [item += 1 for item in list if item >= 1]
            

            【讨论】:

              猜你喜欢
              • 2019-09-13
              • 2011-07-27
              • 2010-11-03
              • 2018-07-29
              • 2011-08-14
              • 2017-12-16
              • 1970-01-01
              相关资源
              最近更新 更多