Pythonicznym sposobem na to jest:
x = [None] * numElements
lub jakąkolwiek wartością domyślną, którą chcesz zastosować, np
bottles = [Beer()] * 99
sea = [Fish()] * many
vegetarianPizzas = [None] * peopleOrderingPizzaNotQuiche
[Edycja: Zastrzeżenie strzeże[Beer()] * 99
składni tworzy jedną Beer
, a następnie wypełnia się tablicę 99 odniesienia do tego samego pojedynczego przykład]
Domyślne podejście Pythona może być dość wydajne, chociaż wydajność maleje wraz ze wzrostem liczby elementów.
Porównać
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)
z
#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);
}
W moim systemie Windows 7 i7 64-bitowy Python daje
Elements: 100000, Iterations: 144
doAppend: 3587.204933ms
doAllocate: 2701.154947ms
doGenerator: 1721.098185ms
Podczas gdy C ++ daje (zbudowany z MSVC, 64-bit, włączone optymalizacje)
Elements: 100000, Iterations: 144
doAppend: 74.0042ms
doReserve: 27.0015ms
doAllocate: 5.0003ms
Kompilacja debugowania w C ++ daje:
Elements: 100000, Iterations: 144
doAppend: 2166.12ms
doReserve: 2082.12ms
doAllocate: 273.016ms
Chodzi o to, że dzięki Python możesz osiągnąć 7-8% poprawę wydajności, a jeśli myślisz, że piszesz aplikację o wysokiej wydajności (lub jeśli piszesz coś, co jest używane w serwisie internetowym lub coś takiego), to nie można tego wąchać, ale może trzeba przemyśleć swój wybór języka.
Poza tym kod Pythona nie jest tak naprawdę kodem Pythona. Przejście na kod naprawdę Pythonesque zapewnia lepszą wydajność:
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)
Co daje
Elements: 100000, Iterations: 144
doAppend: 2153.122902ms
doAllocate: 1346.076965ms
doGenerator: 1614.092112ms
(w 32-bitowym programie doGenerator działa lepiej niż doAllocate).
Tutaj różnica między doAppend i doAllocate jest znacznie większa.
Oczywiście różnice tutaj obowiązują tylko wtedy, gdy robisz to więcej niż kilka razy lub jeśli robisz to w mocno obciążonym systemie, w którym liczby te zostaną skalowane o rzędy wielkości, lub jeśli masz do czynienia z znacznie większe listy.
Chodzi o to: zrób to pythonowy sposób, aby uzyskać najlepszą wydajność.
Ale jeśli martwisz się ogólną wydajnością na wysokim poziomie, Python jest niewłaściwym językiem. Najbardziej podstawowym problemem jest to, że wywołania funkcji Pythona tradycyjnie były do 300 razy wolniejsze niż w innych językach ze względu na funkcje Pythona, takie jak dekoratory itp. ( Https://wiki.python.org/moin/PythonSpeed/PerformanceTips#Data_Aggregation#Data_Aggregation ).