【问题标题】:nesting python list comprehensions to construct a list of lists嵌套python列表推导以构造列表列表
【发布时间】:2009-12-30 20:00:16
【问题描述】:

我是一个 python 新手,在理解嵌套列表推导时遇到了麻烦。我正在尝试编写一些代码来读取文件并为每行的每个字符构造一个列表。

所以如果文件包含

xxxcd
cdcdjkhjasld
asdasdxasda

结果列表将是:

[
['x','x','x','c','d']
['c','d','c','d','j','k','h','j','a','s','l','d'] ['a','s','d','a','s','d','x','a','s','d','a']
]

我已经编写了以下代码,并且它可以工作,但我有一种唠叨的感觉,我应该能够编写一个嵌套列表推导式,以便用更少的代码行来做到这一点。任何建议将不胜感激。

data = []
f = open(file,'r')
for line in f:
    line = line.strip().upper()
    list = []
    for c in line:
        list.append(c)
    data.append(list)

【问题讨论】:

  • 为了简单性和可读性,有很多话要说。虽然您可能能够通过嵌套推导获得相同的效果,但您也可能最终会无意中混淆某些内容。
  • 另外,没有名为 file 的变量。我知道fileopen 是同义词,但如果你需要测试,比如isinstance(f, file),你会很抱歉。也许叫它filename?名为 list 的变量也是如此。

标签: python list-comprehension


【解决方案1】:

这应该会有所帮助(您可能必须尝试使用​​它来去除换行符或根据需要设置格式,但基本想法应该可行):

f = open(r"temp.txt")
[[c for c in line] for line in f]

【讨论】:

  • 谁能解释这种形式的内在理解是如何运作的?我一直在为此苦苦挣扎。
【解决方案2】:

在您的情况下,您可以使用 list 构造函数来处理内部循环并使用列表推导来处理外部循环。比如:

f = open(file)
data = [list(line.strip().upper()) for line in f]

给定一个字符串作为输入,列表构造函数将创建一个列表,其中字符串的每个字符都是列表中的单个元素。

列表推导在功能上等同于:

data = []
for line in f:
    data.append(list(line.strip().upper()))

【讨论】:

    【解决方案3】:

    这是列表理解的一级。

    data = []
    f = open(file,'r')
    
    for line in f:
        data.append([ch for ch in line.strip().upper()])
    

    但我们可以一次性完成所有事情:

    f = open(file, 'rt')
    data = [list(line.strip().upper()) for line in f]
    

    这是使用list() 将字符串转换为单字符串列表。我们还可以使用嵌套列表推导,并将open() 内联:

    data = [[ch for ch in line.strip().upper()] for line in open(file, 'rt')]
    

    不过,在这一点上,我认为列表推导有损正在发生的事情的易读性。

    对于复杂的处理,例如列表中的列表,您可能希望对外层使用 for 循环,对内层循环使用列表推导。

    另外,正如 Chris Lutz 在评论中所说,在这种情况下,确实没有理由明确地将每一行拆分为字符列表。您始终可以将字符串视为列表,并且可以将字符串方法与字符串一起使用,但不能将字符串方法与列表一起使用。 (好吧,您可以使用''.join() 将列表重新加入字符串,但为什么不将其保留为字符串?)

    【讨论】:

      【解决方案4】:
      data = [list(line.strip().upper()) for line in open(file,'r')]
      

      【讨论】:

      • +1,但我宁愿将列表中的每个条目都保留为字符串。由于字符串可以被索引,所以您仍然可以将其视为二维字符数组。仅当 OP 计划对行进行更改时,才需要 list(...) 部分。
      【解决方案5】:

      字符串和字符列表之间唯一真正重要的区别是字符串是不可变的。您可以像列出列表一样迭代和切片字符串。而且将字符串作为字符串处理要方便得多,因为它们支持字符串方法而列表不支持。

      所以对于大多数应用程序,我不会费心将data 中的项目转换为列表;我会这样做:

      data = [line.strip() for line in open(filename, 'r')]
      

      当我需要将data 中的字符串作为可变列表进行操作时,我会使用list 来转换它们,并使用join 将它们放回去,例如:

      data[2] = ''.join(sorted(list(data[2])))
      

      当然,如果您要对这些字符串做的只是修改它们,那么继续,将它们存储为列表。

      【讨论】:

        【解决方案6】:
        >>> f = file('teste.txt')
        >>> print map(lambda x: [c for c in x][:-1], f)
        [['x', 'x', 'x', 'c', 'd'], ['c', 'd', 'c', 'd', 'j', 'k', 'h', 'j', 'a', 's', 'l', 'd'], ['a', 's', 'd', 'a', 's', 'd', 'x', 'a', 's', 'd']]
        

        【讨论】:

          【解决方案7】:

          首先,您可以将 line.strip().upper() 部分与外部 for 循环结合起来,如下所示:

          for line in [l.strip().upper() for l in f]:
              # do stuff
          

          然后你可以将字符的迭代变成一个列表理解,但它不会更短或更清晰。做你在那里做的最整洁的方法是:

          list(someString)
          

          你可以这样做:

          data = [list(l.strip().upper()) for l in f]
          

          我不知道它是否很好地说明了你的意图。错误处理也是个问题,中途出现问题整个表达式都会死掉。


          如果您不需要将整个文件和所有行存储在内存中,则可以将其制成生成器表达式。这在处理大文件时非常有用,您一次只需要处理一个块。生成器表达式使用括​​号代替,如下所示:

          data = (list(l.strip().upper()) for l in f)
          

          data 将成为一个生成器,它为文件中的每一行运行表达式,但仅当您对其进行迭代时;将其与将在内存中创建一个巨大列表的列表理解进行比较。请注意,data 不是列表,而是生成器,更类似于 C++ 中的迭代器或 C# 中的 IEnumerator。

          可以轻松地将生成器输入到列表中:list(someGenerator) 这会在某种程度上破坏目的,但有时是必需的。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-08-09
            • 2017-02-17
            • 2019-10-04
            • 2016-03-26
            • 1970-01-01
            • 1970-01-01
            • 2010-11-07
            相关资源
            最近更新 更多