Pythonic путь для этого:
x = [None] * numElements
или любое другое значение по умолчанию, с которым вы хотите подготовиться, например
bottles = [Beer()] * 99
sea = [Fish()] * many
vegetarianPizzas = [None] * peopleOrderingPizzaNotQuiche
[EDIT: Caveat Emptor[Beer()] * 99
синтаксис создает один Beer
, а затем заполняет массив с 99 ссылками на тот же единственный экземпляр]
Подход Python по умолчанию может быть довольно эффективным, хотя эта эффективность снижается по мере увеличения количества элементов.
сравнить
import time
class Timer(object):
def __enter__(self):
self.start = time.time()
return self
def __exit__(self, *args):
end = time.time()
secs = end - self.start
msecs = secs * 1000 # millisecs
print('%fms' % msecs)
Elements = 100000
Iterations = 144
print('Elements: %d, Iterations: %d' % (Elements, Iterations))
def doAppend():
result = []
i = 0
while i < Elements:
result.append(i)
i += 1
def doAllocate():
result = [None] * Elements
i = 0
while i < Elements:
result[i] = i
i += 1
def doGenerator():
return list(i for i in range(Elements))
def test(name, fn):
print("%s: " % name, end="")
with Timer() as t:
x = 0
while x < Iterations:
fn()
x += 1
test('doAppend', doAppend)
test('doAllocate', doAllocate)
test('doGenerator', doGenerator)
с участием
#include <vector>
typedef std::vector<unsigned int> Vec;
static const unsigned int Elements = 100000;
static const unsigned int Iterations = 144;
void doAppend()
{
Vec v;
for (unsigned int i = 0; i < Elements; ++i) {
v.push_back(i);
}
}
void doReserve()
{
Vec v;
v.reserve(Elements);
for (unsigned int i = 0; i < Elements; ++i) {
v.push_back(i);
}
}
void doAllocate()
{
Vec v;
v.resize(Elements);
for (unsigned int i = 0; i < Elements; ++i) {
v[i] = i;
}
}
#include <iostream>
#include <chrono>
using namespace std;
void test(const char* name, void(*fn)(void))
{
cout << name << ": ";
auto start = chrono::high_resolution_clock::now();
for (unsigned int i = 0; i < Iterations; ++i) {
fn();
}
auto end = chrono::high_resolution_clock::now();
auto elapsed = end - start;
cout << chrono::duration<double, milli>(elapsed).count() << "ms\n";
}
int main()
{
cout << "Elements: " << Elements << ", Iterations: " << Iterations << '\n';
test("doAppend", doAppend);
test("doReserve", doReserve);
test("doAllocate", doAllocate);
}
На моем Windows 7 i7 64-битный Python дает
Elements: 100000, Iterations: 144
doAppend: 3587.204933ms
doAllocate: 2701.154947ms
doGenerator: 1721.098185ms
В то время как C ++ дает (построен с MSVC, 64-битный, оптимизация включена)
Elements: 100000, Iterations: 144
doAppend: 74.0042ms
doReserve: 27.0015ms
doAllocate: 5.0003ms
Отладочная сборка C ++ производит:
Elements: 100000, Iterations: 144
doAppend: 2166.12ms
doReserve: 2082.12ms
doAllocate: 273.016ms
Дело в том, что с Python вы можете добиться повышения производительности на 7-8%, и если вы думаете, что пишете высокопроизводительное приложение (или если вы пишете что-то, что используется в веб-сервисе или чем-то еще), то это не должно быть обнюхено, но вам, возможно, придется пересмотреть свой выбор языка.
Кроме того, код Python здесь не совсем код Python. Переход на действительно Pythonesque код здесь дает лучшую производительность:
import time
class Timer(object):
def __enter__(self):
self.start = time.time()
return self
def __exit__(self, *args):
end = time.time()
secs = end - self.start
msecs = secs * 1000 # millisecs
print('%fms' % msecs)
Elements = 100000
Iterations = 144
print('Elements: %d, Iterations: %d' % (Elements, Iterations))
def doAppend():
for x in range(Iterations):
result = []
for i in range(Elements):
result.append(i)
def doAllocate():
for x in range(Iterations):
result = [None] * Elements
for i in range(Elements):
result[i] = i
def doGenerator():
for x in range(Iterations):
result = list(i for i in range(Elements))
def test(name, fn):
print("%s: " % name, end="")
with Timer() as t:
fn()
test('doAppend', doAppend)
test('doAllocate', doAllocate)
test('doGenerator', doGenerator)
Который дает
Elements: 100000, Iterations: 144
doAppend: 2153.122902ms
doAllocate: 1346.076965ms
doGenerator: 1614.092112ms
(в 32-битном doGenerator работает лучше, чем doAllocate).
Здесь разрыв между doAppend и doAllocate значительно больше.
Очевидно, что различия здесь действительно применимы, только если вы делаете это более чем несколько раз или если вы делаете это в сильно загруженной системе, где эти числа будут уменьшены на порядки или если вы имеете дело с значительно большие списки.
Суть здесь: сделать это питонским способом для лучшей производительности.
Но если вы беспокоитесь об общей производительности на высоком уровне, Python - не тот язык. Наиболее фундаментальная проблема заключается в том, что вызовы функций Python традиционно были в 300 раз медленнее, чем другие языки, из-за таких функций Python, как декораторы и т. Д. ( Https://wiki.python.org/moin/PythonSpeed/PerformanceTips#Data_Aggregation#Data_Aggregation ).