Python List против Array - когда использовать?

375

Если вы создаете одномерный массив, вы можете реализовать его в виде списка или использовать модуль «массив» в стандартной библиотеке. Я всегда использовал списки для 1d массивов.

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

Это для оптимизации производительности и памяти, или я упускаю что-то очевидное?

Кори Голдберг
источник

Ответы:

438

По сути, списки Python очень гибкие и могут содержать совершенно разнородные произвольные данные, и к ним можно очень эффективно добавлять в амортизированное постоянное время . Если вам нужно сокращать и расширять свой список эффективно и без лишних хлопот, они - путь. Но они используют намного больше места, чем массивы C .

array.arrayТипа, с другой стороны, это всего лишь тонкая оболочка на массивах C. Он может содержать только однородные данные одного типа, поэтому он использует только sizeof(one object) * lengthбайты памяти. В основном, вы должны использовать его, когда вам нужно представить массив C расширению или системному вызову (например, ioctlили fctnl).

array.arrayтакже является разумным способом представления изменяемой строки в Python 2.x ( array('B', bytes)). Тем не менее, Python 2.6+ и 3.x предлагает изменяемую строку байтов какbytearray .

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

Короче говоря : array.arrayполезно, когда вам нужен однородный массив данных C по причинам, отличным от математического .

Дэн Ленски
источник
9
Имеет ли numpy.ndarray тот же объем памяти, что и array.array?
Гордон Бин
6
@ Гордон, он должен быть очень похожим в случае большого непрерывного массива: им обоим потребуется sizeof(element)× (количество элементов) байтов, плюс небольшой фиксированный заголовок для служебных данных. Тем не менее, ndarray имеет некоторые расширенные опции для работы с разрозненными и разреженными массивами, и я думаю, что некоторые подключаемые стратегии для выделения памяти для больших массивов ... некоторые из этих расширенных функций сделают его менее оперативным, в то время как другие улучшат производительность, используя больше Память.
Дэн Ленски
Также полезно, когда возникает проблема с памятью, например, при программировании микроконтроллеров с помощью микропифона
janscas
Можно искать i-й элемент массива за постоянное время, тогда как в связанном списке он принимает порядок 'n' в худшем случае. Каково время поиска i-го элемента в списке Python?
Nithish Inpursuit Ofhappiness
7
@NithishInpursuitOfhappiness, список Python не является связанным списком. Внутри он представлен в виде массива и имеет те же характеристики сложности времени, что и ArrayList в Java. Таким образом, получение и установка i-го элемента списка Python занимает постоянное время . Добавление элемента в список Python занимает амортизированное постоянное время, поскольку размер массива удваивается, когда ему не хватает места. Вставка элемента в середину списка Python или удаление из него занимает O (n) времени, потому что элементы должны быть смещены. Для справки: wiki.python.org/moin/TimeComplexity
geofflee
66

Почти во всех случаях нормальный список является правильным выбором. Модуль массивов больше похож на тонкую оболочку над массивами Си, которая дает вам строго типизированные контейнеры (см. Документы ), с доступом к более C-подобным типам, таким как подписанные / неподписанные короткие или двойные, которые не являются частью встроенного в типах. Я бы сказал, использовать модуль массива, только если он вам действительно нужен, во всех остальных случаях придерживайтесь списков.

Андре
источник
3
Возможно, никогда не использовал это действительно хотя, но было бы интересно выполнить некоторые микро тесты.
Андре
13
На самом деле, я сделал быстрый тест - я рассчитал время суммирования списка с 100M записями и того же теста с соответствующим массивом, и этот список был на 10% быстрее.
Мо
38
Списки быстрее, потому что операции с «необработанными» данными массива должны постоянно создавать и уничтожать объекты Python при чтении или записи в массив.
Цот
7
@Moe, как я указывал в моем ответе выше, Python встроенный arrayв не предназначен для выполнения математика . Если вы попытаетесь использовать NumPy ndarrayдля суммирования массива из 10 ^ 8 чисел, он будет полностью listудален. У @tzot есть правильное представление о том, почему встроенная система arrayработает медленно по математике.
Дэн Ленски
2
Я только что проверил, NumPy 86,6x быстрее на моей машине.
Отметить
53

Модуль массива - это одна из тех вещей, в которых вы, вероятно, не нуждаетесь, если не знаете, зачем его использовать (и обратите внимание, что я не пытаюсь сказать это снисходительно!) , Большую часть времени модуль массива используется для взаимодействия с кодом Си. Чтобы дать вам более прямой ответ на ваш вопрос о производительности:

Массивы более эффективны, чем списки для некоторых целей. Если вам нужно выделить массив, который, как вы ЗНАЕТЕ, не изменится, тогда массивы могут быть быстрее и использовать меньше памяти. GvR имеет анекдот по оптимизации, в котором модуль массива оказывается победителем (долго читал, но оно того стоит).

С другой стороны, одна из причин, по которой списки занимают больше памяти, чем массивы, заключается в том, что python выделит несколько дополнительных элементов, когда все выделенные элементы будут использованы. Это означает, что добавление элементов в списки происходит быстрее. Так что, если вы планируете добавлять элементы, список - это путь.

TL; DR Я бы использовал массив, только если у вас была исключительная потребность в оптимизации или вам нужно взаимодействовать с кодом C (и вы не можете использовать pyrex ).

Джейсон Бейкер
источник
1
+1 за конкретный пример и упоминание скоростной выгоды. Главный ответ заставил меня задуматься: «Есть ли компромисс между временем и памятью?» и "Есть ли смысл в этом, который не очень эзотерический случай с нехваткой памяти?"
leewz
@leewz точно, это следует рассматривать как ответ.
Гаури Шанкар Бадола
21

Это компромисс!

плюсы каждого:

список

  • гибкий
  • может быть неоднородным

массив (напр .: массив numpy)

  • массив равномерных значений
  • гомогенный
  • компактный (по размеру)
  • эффективный (функциональность и скорость)
  • удобный
Мохаммад Махди Кушак Язди
источник
2
вопрос относится к массиву модуля в python; не numy массивы. У них нет много плюсов, кроме эффективности размера. Они не быстрее.
NONONONONO
14

Насколько я понимаю, массивы хранятся более эффективно (то есть как непрерывные блоки памяти по сравнению с указателями на объекты Python), но я не знаю о какой-либо выигрыш в производительности. Кроме того, с массивами вы должны хранить примитивы одного типа, тогда как списки могут хранить что угодно.

Бен Хоффштейн
источник
8

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

Гильтая
источник
6

Если вы собираетесь использовать массивы, рассмотрите пакеты numpy или scipy, которые дают вам массивы с большей гибкостью.

Алекс Ковентри
источник
5

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

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

Массивы также более эффективны для некоторых численных расчетов.

Hortitude
источник
4
Встроенные массивы Python не эффективны с точки зрения производительности, а только с точки зрения памяти.
Цот
Есть случаи, когда массивы более эффективны с точки зрения обработки. Смотрите мой пост ниже: stackoverflow.com/questions/176011/…
Джейсон Бейкер
0

Важное различие между массивом numpy и списком состоит в том, что срезы массива являются представлениями исходного массива. Это означает, что данные не копируются, и любые изменения в представлении будут отражены в исходном массиве.

Вивек
источник
0

Этот ответ подытожит почти все запросы о том, когда использовать List и Array:

  1. Основное различие между этими двумя типами данных заключается в операциях, которые вы можете выполнять над ними. Например, вы можете разделить массив на 3, и он разделит каждый элемент массива на 3. То же самое нельзя сделать со списком.

  2. Список является частью синтаксиса python, поэтому его не нужно объявлять, в то время как вы должны объявить массив перед его использованием.

  3. Вы можете хранить значения разных типов данных в списке (разнородных), тогда как в массиве вы можете хранить значения только одного и того же типа данных (однородных).

  4. Массивы, обладающие большими функциональными возможностями и быстрыми, широко используются для арифметических операций и для хранения большого объема данных - по сравнению со списком.

  5. Массивы занимают меньше памяти по сравнению со списками.

Дипен Гаджар
источник
0

Что касается производительности, вот некоторые цифры, сравнивающие списки, массивы и пустые массивы Python (все с Python 3.7 на Macbook Pro 2017 года). Конечным результатом является то, что список python является самым быстрым для этих операций.

# Python list with append()
np.mean(timeit.repeat(setup="a = []", stmt="a.append(1.0)", number=1000, repeat=5000)) * 1000
# 0.054 +/- 0.025 msec

# Python array with append()
np.mean(timeit.repeat(setup="import array; a = array.array('f')", stmt="a.append(1.0)", number=1000, repeat=5000)) * 1000
# 0.104 +/- 0.025 msec

# Numpy array with append()
np.mean(timeit.repeat(setup="import numpy as np; a = np.array([])", stmt="np.append(a, [1.0])", number=1000, repeat=5000)) * 1000
# 5.183 +/- 0.950 msec

# Python list using +=
np.mean(timeit.repeat(setup="a = []", stmt="a += [1.0]", number=1000, repeat=5000)) * 1000
# 0.062 +/- 0.021 msec

# Python array using += 
np.mean(timeit.repeat(setup="import array; a = array.array('f')", stmt="a += array.array('f', [1.0]) ", number=1000, repeat=5000)) * 1000
# 0.289 +/- 0.043 msec

# Python list using extend()
np.mean(timeit.repeat(setup="a = []", stmt="a.extend([1.0])", number=1000, repeat=5000)) * 1000
# 0.083 +/- 0.020 msec

# Python array using extend()
np.mean(timeit.repeat(setup="import array; a = array.array('f')", stmt="a.extend([1.0]) ", number=1000, repeat=5000)) * 1000
# 0.169 +/- 0.034
Гефест
источник