Инициализация списка известному числу элементов в Python

Сейчас я использую список и ожидаю что-то вроде:

verts = list (1000)

Должен ли я использовать массив вместо?

Ответ 1

Первое, что приходит мне на ум, это:

verts = [None]*1000

Но вам действительно нужно предварительно инициализировать его?

Ответ 2

Не совсем уверен, почему каждый человек испытывает трудности с желанием сделать это - есть несколько сценариев, в которых вы хотите получить список с фиксированным размером. И вы правильно определили, что массивы разумны в этих случаях.

import array
verts=array.array('i',(0,)*1000)

Для не-pythonistas термин (0,)*1000 создает кортеж, содержащий 1000 нулей. Запятая заставляет python распознавать (0) как кортеж, иначе он будет оцениваться как 0.

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

Ответ 3

Один очевидный и, вероятно, не эффективный способ -

verts = [0 for x in range(1000)]

Обратите внимание, что это можно легко расширить до 2-мерного. Например, чтобы получить массив 10x100, вы можете сделать

verts = [[0 for x in range(100)] for y in range(10)]

Ответ 4

Желание инициализировать массив фиксированного размера - вполне приемлемая вещь для любого языка программирования; это не похоже на то, что программист хочет поставить оператор break в течение некоторого (истинного) цикла. Поверьте мне, особенно если элементы просто будут перезаписаны, а не просто добавлены/вычтены, как в случае многих алгоритмов динамического программирования, вы не хотите обходиться с операторами append и проверять, не был ли этот элемент инициализирован еще "на лету" (что много кодовых символов).

object = [0 for x in range(1000)]

Это будет работать для того, чего пытается достичь программист.

Ответ 5

@Steve уже дал хороший ответ на ваш вопрос:

verts = [None] * 1000

Предупреждение: Как указал @Joachim Wuttke, список должен быть инициализирован неизменным элементом. [[]] * 1000 работает не так, как ожидалось, потому что вы получите список из 1000 идентичных списков (аналогично списку из 1000 пунктов в тот же список на C). Неизменяемые объекты, такие как int, str или кортеж, будут работать нормально.

Альтернативы

Изменение размеров списков происходит медленно. Следующие результаты не очень удивляют:

>>> N = 10**6

>>> %timeit a = [None] * N
100 loops, best of 3: 7.41 ms per loop

>>> %timeit a = [None for x in xrange(N)]
10 loops, best of 3: 30 ms per loop

>>> %timeit a = [None for x in range(N)]
10 loops, best of 3: 67.7 ms per loop

>>> a = []
>>> %timeit for x in xrange(N): a.append(None)
10 loops, best of 3: 85.6 ms per loop

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

>>> %timeit a = [x**2 for x in xrange(N)]
10 loops, best of 3: 109 ms per loop

>>> def fill_list1():
    """Not too bad, but complicated code"""
    a = [None] * N
    for x in xrange(N):
        a[x] = x**2
>>> %timeit fill_list1()
10 loops, best of 3: 126 ms per loop

>>> def fill_list2():
    """This is slow, use only for small lists"""
    a = []
    for x in xrange(N):
        a.append(x**2)
>>> %timeit fill_list2()
10 loops, best of 3: 177 ms per loop

Сравнение с numpy

Для огромного набора данных numpy или других оптимизированных библиотек выполняется намного быстрее:

from numpy import ndarray, zeros
%timeit empty((N,))
1000000 loops, best of 3: 788 ns per loop

%timeit zeros((N,))
100 loops, best of 3: 3.56 ms per loop

Ответ 6

Вы можете сделать это:

verts = list(xrange(1000))

Это даст вам список из 1000 элементов в размере и который будет инициализирован со значениями от 0 до 999. Поскольку list делает a __len__ первым, чтобы изменить размер нового списка, он должен быть достаточно эффективным.

Ответ 7

Вам следует использовать тип dict вместо предварительно инициализированного списка. Стоимость поиска в словарях мала и сопоставима со стоимостью доступа к произвольному элементу списка.

И при использовании сопоставления вы можете написать:

aDict = {}
aDict[100] = fetchElement()
putElement(fetchElement(), fetchPosition(), aDict)

И функция putElement может хранить элемент в любой заданной позиции. И если вам нужно проверить, содержит ли ваша коллекция элемент по заданному индексу, более питонов написать:

if anIndex in aDict:
    print "cool!"

Чем:

if not myList[anIndex] is None:
    print "cool!"

Поскольку последний предполагает, что никакой реальный элемент в вашей коллекции не может быть None. И если это произойдет - ваш код неправильно.

И если вам отчаянно нужна производительность и почему вы пытаетесь предварительно инициализировать свои переменные и записываете самый быстрый код - измените свой язык. Самый быстрый код не может быть написан на Python. Вы должны попробовать C вместо этого и реализовать оболочки для вызова вашего предварительно инициализированного и предварительно скомпилированного кода из Python.

Ответ 8

Это:

 list = [8 for i in range(9)]

создает список, элементы инициализируются 8

но это:

list = [0] * 7

создаст 7 списков, у которых есть один элемент

Ответ 9

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

verts = []

Вы действительно видите проблему с производительностью? Если да, то каково узкое место в производительности? Не пытайтесь решить проблему, которой у вас нет. Вероятно, что производительность для динамического заполнения массива до 1000 элементов полностью неактуальна для программы, которую вы действительно пытаетесь написать.

Класс массива полезен, если вещи в вашем списке всегда будут конкретным примитивным типом фиксированной длины (например, char, int, float). Но это также не требует предварительной инициализации.