pythonically地从文件中读取时一次构建2个列表

时间:2016-09-01 10:14:48

标签: python list python-3.x

我正在阅读一个包含数十万个代表图表边缘的数字对的大文件。我想建立2个列表:一个带有前沿,另一个带有反向。

目前我正在做一个明确的for循环,因为我需要对我读过的行进行一些预处理。但是,我想知道是否有更多的pythonic方法来构建这些列表,如列表推导等。

但是,因为我有2个列表,所以我没有看到使用理解来填充它们而无需两次读取文件的方法。

我现在的代码是:

with open('SCC.txt') as data:
    for line in data:
        line = line.rstrip()
        if line:
            edge_list.append((int(line.rstrip().split()[0]), int(line.rstrip().split()[1])))
            reversed_edge_list.append((int(line.rstrip().split()[1]), int(line.rstrip().split()[0])))

4 个答案:

答案 0 :(得分:11)

我会保持你的逻辑,因为它是 Pythonic 方法,而不是 split / rstrip 多次同一行:

with open('SCC.txt') as data:
    for line in data:
        spl = line.split()
        if spl:
            i, j = map(int, spl)
            edge_list.append((i, j))
            reversed_edge_list.append((j, i))

当你已经调用 rstrip 时,调用 rstrip 本身就是多余的,因为当你分裂时,已经删除了空白,所以只拆分一次意味着你节省了很多不必要的工作。

您还可以使用 csv.reader 读取数据,并在只有一个空格分隔后过滤空行:

from csv import reader

with open('SCC.txt') as data:
    edge_list, reversed_edge_list = [], []
    for i, j in filter(None, reader(data, delimiter=" ")):
        i, j = int(i), int(j)
        edge_list.append((i, j))
        reversed_edge_list.append((j, i))

如果有多个空格分隔,您可以使用map(str.split, data)

    for i, j in filter(None, map(str.split, data)):
        i, j = int(i), int(j)

无论你选择什么,都会比过两次数据或多次分割同一行更快。

答案 1 :(得分:5)

无法在一次理解中创建两个列表,因此,不是在两个列表上执行两次相同的操作,一个可行的选项是初始化其中一个然后创建第二个是通过反转第一个中的每个条目。这样你就不会两次迭代文件。

为此,您可以创建第一个列表edge_list并理解(不确定为什么再次调用rsplit ):

edge_list = [tuple(map(int, line.split())) for line in data]

现在浏览每个条目并使用[::-1]将其反转,以便创建其反转的兄弟reverse_edge_list

使用edge_list的模拟数据

edge_list = [(1, 2), (3, 4), (5, 6)]

反转它可能如下所示:

reverse_edge_list = [t[::-1] for t in edge_list]

现在看起来像:

reverse_edge_list
[(2, 1), (4, 3), (6, 5)]

答案 2 :(得分:3)

可能不是更清楚,但更短:

with open('SCC.txt') as data:
    process_line = lambda line, r: (int(line.rstrip().split()[r]), int(line.rstrip().split()[1-r]))

    edge_list, reverved_edge_list = map(list, zip(*[(process_line(line, 0), process_line(line, 1)) 
                                                    for line in data
                                                    if line.rstrip()]))

答案 3 :(得分:3)

这是一个解决方案

测试文件:

In[19]: f = ["{} {}".format(i,j) for i,j in zip(xrange(10), xrange(10, 20))]
In[20]: f
Out[20]: 
['0 10',
 '1 11',
 '2 12',
 '3 13',
 '4 14',
 '5 15',
 '6 16',
 '7 17',
 '8 18',
 '9 19']

使用理解,拉链和地图的一个班轮:

In[27]: l, l2 = map(list,zip(*[(tuple(map(int, x.split())), tuple(map(int, x.split()))[::-1]) for x in f]))
In[28]: l
Out[28]: 
[(0, 10),
 (1, 11),
 (2, 12),
 (3, 13),
 (4, 14),
 (5, 15),
 (6, 16),
 (7, 17),
 (8, 18),
 (9, 19)]
In[29]: l2
Out[29]: 
[(10, 0),
 (11, 1),
 (12, 2),
 (13, 3),
 (14, 4),
 (15, 5),
 (16, 6),
 (17, 7),
 (18, 8),
 (19, 9)]

解释,用[(tuple(map(int, x.split())), tuple(map(int, x.split()))[::-1]) for x in f]我们构建一个列表,其中包含一对元组及其反转形式的元组:

In[24]: [(tuple(map(int, x.split())), tuple(map(int, x.split()))[::-1]) for x in f]
Out[24]: 
[((0, 10), (10, 0)),
 ((1, 11), (11, 1)),
 ((2, 12), (12, 2)),
 ((3, 13), (13, 3)),
 ((4, 14), (14, 4)),
 ((5, 15), (15, 5)),
 ((6, 16), (16, 6)),
 ((7, 17), (17, 7)),
 ((8, 18), (18, 8)),
 ((9, 19), (19, 9))]

zip包含在解压缩形式中我们将元组拆分为主元组,因此我们有2个元组包含第一个中的元组对,而其他元组则相反:

In[25]: zip(*[(tuple(map(int, x.split())), tuple(map(int, x.split()))[::-1]) for x in f])
Out[25]: 
[((0, 10),
  (1, 11),
  (2, 12),
  (3, 13),
  (4, 14),
  (5, 15),
  (6, 16),
  (7, 17),
  (8, 18),
  (9, 19)),
 ((10, 0),
  (11, 1),
  (12, 2),
  (13, 3),
  (14, 4),
  (15, 5),
  (16, 6),
  (17, 7),
  (18, 8),
  (19, 9))]

几乎在那里,我们只使用map将这些元组转换为列表。

修改 正如@PadraicCunningham所说,要过滤空行,只需在理解if x中添加[ ... for x in f if x]