Відповіді:
Це динамічний масив . Практичне підтвердження: індексація займає (звичайно, з надзвичайно малими різницями (0,0013 мкс / с!)) Однаково, незалежно від індексу:
...>python -m timeit --setup="x = [None]*1000" "x[500]"
10000000 loops, best of 3: 0.0579 usec per loop
...>python -m timeit --setup="x = [None]*1000" "x[0]"
10000000 loops, best of 3: 0.0566 usec per loop
Я був би вражений, якби IronPython або Jython використовували пов'язані списки - вони погіршать ефективність багатьох багатьох широко використовуваних бібліотек, побудованих на припущенні, що списки - це динамічні масиви.
x=[None]*1000
, залишаючи оцінку будь-якої можливої різниці в списку досить неточною. Вам потрібно відокремити ініціалізацію:-s "x=[None]*100" "x[0]"
Насправді код C досить простий. Розширюючи один макрос і обрізаючи деякі невідповідні коментарі, основна структура знаходиться в listobject.h
, яка визначає список як:
typedef struct {
PyObject_HEAD
Py_ssize_t ob_size;
/* Vector of pointers to list elements. list[0] is ob_item[0], etc. */
PyObject **ob_item;
/* ob_item contains space for 'allocated' elements. The number
* currently in use is ob_size.
* Invariants:
* 0 <= ob_size <= allocated
* len(list) == ob_size
* ob_item == NULL implies ob_size == allocated == 0
*/
Py_ssize_t allocated;
} PyListObject;
PyObject_HEAD
містить номер відліку та ідентифікатор типу. Отже, це вектор / масив, який перенаселяє. Код для зміни розміру такого масиву, коли він заповнений, є в listobject.c
. Він насправді не подвоює масив, а зростає шляхом виділення
new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6);
new_allocated += newsize;
до ємності щоразу, де newsize
потрібний розмір (не обов'язково allocated + 1
тому, що ви можете extend
за довільною кількістю елементів замість того append
, щоб виводити їх один за одним).
Дивіться також поширені питання Python .
array
слід віддавати перевагу модулю або NumPy.
Це залежить від реалізації, але IIRC:
ArrayList
Таким чином, всі вони мають O (1) випадковий доступ.
O(1)
для індексації списків є досить поширеним і справедливим припущенням, жодна реалізація не наважилася б його порушити.
Я б запропонував статтю Лорана Люса "Реалізація списку Python" . Мені було дуже корисно, оскільки автор пояснює, як список реалізований у CPython та використовує для цього чудові діаграми.
Перерахуйте структуру об'єкта C
Об'єкт списку в CPython представлений наступною структурою C.
ob_item
- це список покажчиків на елементи списку. виділено - кількість слотів, виділених у пам'яті.typedef struct { PyObject_VAR_HEAD PyObject **ob_item; Py_ssize_t allocated; } PyListObject;
Важливо помітити різницю між виділеними слотами та розмірами списку. Розмір списку такий самий, як
len(l)
. Кількість виділених слотів - це те, що було виділено в пам'яті. Часто ви побачите, що виділений може бути більшим за розмір. Це уникає необхідності викликуrealloc
кожного разу, коли нові елементи додаються до списку.
...
Додавати
Ми додати ціле число у списку:
l.append(1)
. Що станеться?
Ми по- , як і раніше, додавши ще один елемент:
l.append(2)
.list_resize
називається n + 1 = 2, але оскільки розмір, що виділяється, дорівнює 4, не потрібно виділяти більше пам'яті. Те саме відбувається, коли ми додаємо ще 2 цілих числа:l.append(3)
,l.append(4)
. Наступна схема показує, що ми маємо досі.
...
Вставити
Вставмо нове ціле число (5) у позицію 1:
l.insert(1,5)
і подивимось, що відбувається всередині.
...
Поп
Коли з'являється останній елемент:
l.pop()
,listpop()
називається.list_resize
називається всередині,listpop()
і якщо новий розмір становить менше половини виділеного розміру, тоді список зменшується.Ви можете помітити, що слот 4 все ще вказує на ціле число, але найважливішим є розмір списку, який зараз є 4. Давайте ще один елемент. В
list_resize()
, розмір - 1 = 4 - 1 = 3 менше половини виділених слотів, тому список скорочується до 6 слотів, а новий розмір списку зараз становить 3.Ви можете помітити, що слоти 3 і 4 все ще вказують на деякі цілі числа, але важливим є розмір списку, який зараз становить 3.
...
Видалити Python список об'єктів має метод для видалення певного елемента:
l.remove(5)
.
aggregation
не, ні composition
. Мені б хотілося, щоб був і склад композиції.
Згідно з документацією ,
Списки Python - це дійсно масиви змінної довжини, а не списки, пов'язані у стилі Lisp.
Як зазначали інші вище, списки (коли помітно великі) реалізуються шляхом виділення фіксованого обсягу простору, і, якщо цей простір повинен заповнити, виділення більшої кількості простору та копіювання на елементи.
Щоб зрозуміти, чому метод амортизований O (1), не втрачаючи загальності, припустимо, що ми вставили a = 2 ^ n елементів, і тепер нам потрібно подвоїти нашу таблицю на розмір 2 ^ (n + 1). Це означає, що в даний час ми робимо 2 ^ (n + 1) операції. Останню копію ми зробили 2 ^ n операцій. До цього ми робили 2 ^ (n-1) ... аж до 8,4,2,1. Тепер, якщо додати їх, отримаємо 1 + 2 + 4 + 8 + ... + 2 ^ (n + 1) = 2 ^ (n + 2) - 1 <4 * 2 ^ n = O (2 ^ n) = O (a) загальна кількість вставок (тобто O (1) амортизований час). Також слід зазначити, що якщо таблиця дозволяє видалити, зменшення таблиці повинно здійснюватися з різним фактором (наприклад, 3x)
Список в Python - це щось на зразок масиву, де ви можете зберігати кілька значень. Список є змінним, це означає, що ви можете його змінити. Що важливіше, що ви повинні знати, коли ми створюємо список, Python автоматично створює reference_id для цієї змінної списку. Якщо ви зміните її, призначивши інші змінні, головний список буде змінено. Спробуємо з прикладом:
list_one = [1,2,3,4]
my_list = list_one
#my_list: [1,2,3,4]
my_list.append("new")
#my_list: [1,2,3,4,'new']
#list_one: [1,2,3,4,'new']
Ми додаємо, my_list
але наш основний список змінився. Цей середній список не призначається як присвоєння списку копій як його посилання.