Стройте 2 списка за один раз при чтении из файла, пифонически

Я читаю большой файл с сотнями тысяч пар чисел, представляющих края графа. Я хочу построить 2 списка, поскольку я иду: один с передними краями и один с обратным.

В настоящее время я выполняю явный цикл for, потому что мне нужно выполнить предварительную обработку строк, которые я читал. Тем не менее, мне интересно, есть ли еще более питонический подход к построению этих списков, например, понимание списков и т.д.

Но, поскольку у меня есть 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])))

Ответ 1

Я бы сохранил вашу логику, так как это подход Pythonic просто не разбивал/прокручивал одну и ту же строку несколько раз:

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, когда вы уже вызвали его, лишний сам по себе еще больше, когда вы раскалываетесь, поскольку это уже удалит пробел, поэтому расщепление только один раз означает, что вы сохраняете много ненужной работы.

Вы также можете использовать 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)

Независимо от того, что вы выберете, вы будете быстрее, чем перебирать данные дважды, или несколько раз разбивая строки с именами.

Ответ 2

Вы не можете создать два списка в одном понимании, поэтому вместо выполнения одних и тех же операций дважды в двух списках одним жизнеспособным вариантом будет инициализация одного из них, а затем создание второго, путем изменения каждой записи в первый. Таким образом, вы не повторяете файл дважды.

С этой целью вы можете создать первый список 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)]

Ответ 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()]))

Ответ 4

Вот решение

Тестовый файл:

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 для преобразования этих кортежей в списки.

EDIT: как попросил @PadraicCunningham, для фильтрации пустых строк просто добавьте if x в понимание [ ... for x in f if x]