Пифонический способ для этого:
x = [None] * numElements
Или любое другое значение по умолчанию, которое вы хотите предварительно заполнить, например
bottles = [Beer()] * 99
sea = [Fish()] * many
vegetarianPizzas = [None] * peopleOrderingPizzaNotQuiche
(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);
}
На моем Core i7 Windows 7 64-разрядный Python дает
Elements: 100000, Iterations: 144
doAppend: 3587.204933ms
doAllocate: 2701.154947ms
doGenerator: 1721.098185ms
Хотя C ++ дает (построено с помощью Microsoft Visual C ++, 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. Переключение на истинно питонский код здесь дает лучшую производительность:
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, как декораторы и т. Д. (PythonSpeed / PerformanceTips, агрегирование данных).
person
kfsone
schedule
11.06.2014
whileциклов с неясными или недетерминированными условиями завершенияitertoolsи генераторы могут в большинстве случаев спасти логику обратно, чтобы перечислить область понимания. - person ggorlen   schedule 08.11.2020