Python: самый быстрый способ создать список из n списков

97

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

[[],[],[]...]

Из-за того, как Python работает со списками в памяти, это не работает:

[[]]*n

Это создает, [[],[],...]но каждый элемент является одним и тем же списком:

d = [[]]*n
d[0].append(1)
#[[1],[1],...]

Что-то вроде понимания списка работает:

d = [[] for x in xrange(0,n)]

Но при этом для создания цикла используется виртуальная машина Python. Есть ли способ использовать подразумеваемый цикл (используя то, что он написан на C)?

d = []
map(lambda n: d.append([]),xrange(0,10))

На самом деле это медленнее. :(

закуска
источник
1
Я был бы удивлен, если есть что-то существенно быстрее, чем d = [[] for x in xrange(0,n)]. Вам нужно либо явно выполнить цикл в Python, либо повторно вызывать функцию Python / лямбда (что должно быть медленнее). Но все же надеюсь, что кто-то опубликует что-то, что показывает, что я неправ :).
MAK
1
timeitЧто вы узнали, когда вы измерили их с помощью ?
S.Lott
Я только что подтвердил, что map(lambda x: [], xrange(n))это медленнее, чем понимание списка.
Эндрю Кларк

Ответы:

105

Вероятно, единственный способ, который немного быстрее, чем

d = [[] for x in xrange(n)]

является

from itertools import repeat
d = [[] for i in repeat(None, n)]

Ему не нужно создавать новый intобъект на каждой итерации, и на моей машине он работает примерно на 15% быстрее.

Изменить : используя NumPy, вы можете избежать цикла Python, используя

d = numpy.empty((n, 0)).tolist()

но на самом деле это в 2,5 раза медленнее, чем понимание списка.

Свен Марнах
источник
Как насчет map(lambda x:[], repeat(None,n))?
PaulMcG 01
3
@Paul: Это снова будет намного медленнее из-за накладных расходов на вызов функции лямбда-выражения.
Sven Marnach 01
Разве это не должно быть обновлено сейчас, когда диапазон отличается в Python 3?
beruic
@beruic Я просто цитирую код из вопроса в первом блоке кода, так что менять это не имеет смысла.
Sven Marnach
12

В списочных фактически реализованы более эффективен , чем явное зацикливании (см на disвыходе для примера функций ) и mapспособ имеет права ссылаться на ophaque вызываемого объекта на каждую итерации которых понесенную значительные накладные накладные расходы.

Тем не менее, [[] for _dummy in xrange(n)]это правильный способ сделать это, и ни одна из крошечных (если вообще существует) разница в скорости между различными другими способами не должна иметь значения. Если, конечно, вы не тратите на это большую часть своего времени - но в этом случае вам следует вместо этого поработать над своими алгоритмами. Как часто вы составляете эти списки?


источник
5
Пожалуйста, не _используйте имя переменной! В противном случае хороший ответ :)
Sven Marnach 01
11
@Sven: Почему бы и нет? Обычно он используется для неиспользуемых переменных (если бы он был вызван i, я бы, например, искал, где он используется). Единственная ловушка заключается в том, что он затеняет сохранение _последнего результата в REPL ... и это только в случае 2.x, где происходит утечка понимания списка.
Не очень часто, поэтому я пошел дальше и использовал понимание списка. Подумал, что было бы интересно посмотреть, что люди скажут. Я видел выступление Dropbox на PyCon с использованием itertools.imap вместо цикла for для обновления хэша md5 абсурдно большое количество раз, и с тех пор я был немного одержим циклами C.
munchybunch
12
Самая важная причина не использовать его в том, что он сбивает людей с толку, заставляя их думать, что это какой-то особый синтаксис. И помимо конфликта с _в интерактивном интерпретаторе, он также конфликтует с общим псевдонимом gettext. Если вы хотите прояснить, что переменная является фиктивной переменной, вызывайте ее dummy, а не _.
Sven Marnach 01
12

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

Метод 1: концептуальный

X2=[]
X1=[1,2,3]
X2.append(X1)
X3=[4,5,6]
X2.append(X3)
X2 thus has [[1,2,3],[4,5,6]] ie a list of lists. 

Метод 2: формальный и расширяемый

Еще один элегантный способ сохранить список как список списков с разными числами, который он считывает из файла. (В этом файле находится поезд набора данных) Train - это набор данных, например, с 50 строками и 20 столбцами. т.е. Train [0] дает мне 1-ю строку файла csv, train [1] дает мне 2-ю строку и так далее. Я заинтересован в том, чтобы разделить набор данных с 50 строками как один список, за исключением столбца 0, который является моей объясненной переменной здесь, поэтому его необходимо удалить из исходного набора данных поезда, а затем масштабировать список после списка, то есть список списка . Вот код, который это делает.

Обратите внимание, что я читаю от «1» внутреннего цикла, поскольку меня интересуют только объясняющие переменные. И я повторно инициализирую X1 = [] в другом цикле, иначе X2.append ([0: (len (train [0]) - 1)]) будет перезаписывать X1 снова и снова - кроме того, это более эффективно с точки зрения памяти.

X2=[]
for j in range(0,len(train)):
    X1=[]
    for k in range(1,len(train[0])):
        txt2=train[j][k]
        X1.append(txt2)
    X2.append(X1[0:(len(train[0])-1)])
экта
источник
4

Для создания списка и списка списков используйте синтаксис ниже

     x = [[] for i in range(10)]

это создаст 1-мерный список и для его инициализации поместите число в [[число] и установите длину списка, поместите длину в диапазон (длина)

  • Для создания списка списков используйте приведенный ниже синтаксис.
    x = [[[0] for i in range(3)] for i in range(10)]

это инициализирует список списков с размером 10 * 3 и значением 0

  • Для доступа / управления элементом
    x[1][5]=value
Вакас Ахмед
источник
2

Поэтому я провел несколько сравнений скорости, чтобы выбрать самый быстрый способ. Составление списков действительно очень быстрое. Единственный способ приблизиться - это не допустить отключения байт-кода во время построения списка. Первой моей попыткой был следующий метод, который в принципе казался более быстрым:

l = [[]]
for _ in range(n): l.extend(map(list,l))

(разумеется, производит список длиной 2 ** n) Это построение в два раза медленнее, чем понимание списка, согласно timeit, как для коротких, так и для длинных (миллион) списков.

Моя вторая попытка заключалась в том, чтобы использовать starmap для вызова конструктора списка для меня. Есть одна конструкция, которая, кажется, запускает конструктор списка на максимальной скорости, но все же медленнее, но только на крошечную величину:

from itertools import starmap
l = list(starmap(list,[()]*(1<<n)))

Достаточно интересно, что время выполнения предполагает, что это последний вызов списка, который замедляет решение starmap, поскольку время его выполнения почти точно равно скорости:

l = list([] for _ in range(1<<n))

Моя третья попытка была предпринята, когда я понял, что list (()) также создает список, поэтому я попробовал на вид простой:

l = list(map(list, [()]*(1<<n)))

но это было медленнее, чем вызов карты звездного неба.

Вывод: для маньяков скорости: используйте понимание списка. Вызывайте функции только при необходимости. Используйте встроенные команды.

Юрьен Бос
источник