Лучший и/или быстрый способ создания списков в python
В python, насколько мне известно, существует как минимум 3-4 способа создания и инициализации списков заданного размера:
Простой цикл с append
:
my_list = []
for i in range(50):
my_list.append(0)
Простой цикл с +=
:
my_list = []
for i in range(50):
my_list += [0]
Учет списка:
my_list = [0 for i in range(50)]
Список и целочисленное умножение:
my_list = [0] * 50
В этих примерах я не думаю, что была бы разница в производительности, учитывая, что в списках есть только 50 элементов, но что, если мне нужен список из миллиона элементов? Будет ли использование xrange
сделать какие-либо улучшения? Какой предпочтительный/самый быстрый способ создания и инициализации списков в python?
Ответы
Ответ 1
Позвольте запустить несколько тестов времени с помощью timeit.timeit
:
>>> from timeit import timeit
>>>
>>> # Test 1
>>> test = """
... my_list = []
... for i in xrange(50):
... my_list.append(0)
... """
>>> timeit(test)
22.384258893239178
>>>
>>> # Test 2
>>> test = """
... my_list = []
... for i in xrange(50):
... my_list += [0]
... """
>>> timeit(test)
34.494779364416445
>>>
>>> # Test 3
>>> test = "my_list = [0 for i in xrange(50)]"
>>> timeit(test)
9.490926919482774
>>>
>>> # Test 4
>>> test = "my_list = [0] * 50"
>>> timeit(test)
1.5340533503559755
>>>
Как вы можете видеть выше, последний метод является самым быстрым на сегодняшний день.
Однако он должен использоваться только с неизменяемыми элементами (такими как целые числа). Это связано с тем, что он создаст список со ссылками на один и тот же элемент.
Ниже приведена демонстрация:
>>> lst = [[]] * 3
>>> lst
[[], [], []]
>>> # The ids of the items in `lst` are the same
>>> id(lst[0])
28734408
>>> id(lst[1])
28734408
>>> id(lst[2])
28734408
>>>
Такое поведение очень часто нежелательно и может привести к ошибкам в коде.
Если у вас есть изменяемые элементы (например, списки), вы должны использовать по-прежнему очень быстрое понимание списка:
>>> lst = [[] for _ in xrange(3)]
>>> lst
[[], [], []]
>>> # The ids of the items in `lst` are different
>>> id(lst[0])
28796688
>>> id(lst[1])
28796648
>>> id(lst[2])
28736168
>>>
* Примечание. Во всех тестах я заменил range
на xrange
. Поскольку последний возвращает итератор, он всегда должен быть быстрее первого.
Ответ 2
Если вы хотите увидеть зависимость с длиной списка n
:
Чистый питон
![enter image description here]()
Я тестировал длину списка до n = 10000, и поведение оставалось неизменным. Таким образом, метод целочисленного умножения является самым быстрым с разницей.
Numpy
Для списков с более чем 300 элементами вы должны рассмотреть numpy.
![enter image description here]()
Код контрольной точки:
import time
def timeit(f):
def timed(*args, **kwargs):
start = time.clock()
for _ in range(100):
f(*args, **kwargs)
end = time.clock()
return end - start
return timed
@timeit
def append_loop(n):
"""Simple loop with append"""
my_list = []
for i in xrange(n):
my_list.append(0)
@timeit
def add_loop(n):
"""Simple loop with +="""
my_list = []
for i in xrange(n):
my_list += [0]
@timeit
def list_comprehension(n):
"""List comprehension"""
my_list = [0 for i in xrange(n)]
@timeit
def integer_multiplication(n):
"""List and integer multiplication"""
my_list = [0] * n
import numpy as np
@timeit
def numpy_array(n):
my_list = np.zeros(n)
import pandas as pd
df = pd.DataFrame([(integer_multiplication(n), numpy_array(n)) for n in range(1000)],
columns=['Integer multiplication', 'Numpy array'])
df.plot()
Gist здесь.
Ответ 3
Есть еще один метод, который, будучи странным, удобен в правильных условиях. Если вам нужно создать один и тот же список много раз (инициализируя матрицу для roguelike pathfinding и связанные с ней вещи в моем случае), вы можете сохранить копию список в кортеже, затем переведите его в список, когда вам это нужно. Это заметно быстрее, чем генерация списка с помощью понятий и, в отличие от умножения списков, работает с вложенными структурами данных.
# In class definition
def __init__(self):
self.l = [[1000 for x in range(1000)] for y in range(1000)]
self.t = tuple(self.l)
def some_method(self):
self.l = list(self.t)
self._do_fancy_computation()
# self.l is changed by this method
# Later in code:
for a in range(10):
obj.some_method()
Voila, на каждой итерации у вас есть свежая копия того же списка в кратчайшие сроки!
Отказ от ответственности:
У меня нет ни малейшего понятия, почему это так быстро или работает независимо от CPython 3.4.