Pythonic способ для этого:
x = [None] * numElements
или любое другое значение по умолчанию, с которым вы хотите подготовиться, например,
bottles = [Beer()] * 99
sea = [Fish()] * many
vegetarianPizzas = [None] * peopleOrderingPizzaNotQuiche
[РЕДАКТИРОВАТЬ: Caveat Emptor Синтаксис [Beer()] * 99
создает one 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, как декораторы и т. Д.