Le Pythonism pour ce qui est
x = elements * [None]
ou quelle que soit la valeur par défaut que vous souhaitez prepop avec.
Python par défaut de l'approche peut être assez efficace, bien que l'efficacité se désintègre comme vous augmentez le nombre d'éléments.
Comparer
le délai d'importation
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)
avec
#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);
}
Sur mon Windows 7 i7, 64-bit Python donne
Elements: 100000, Iterations: 144
doAppend: 3587.204933ms
doAllocate: 2701.154947ms
doGenerator: 1721.098185ms
Tandis que le C++ donne (construit avec MSVC, 64-bit, les Optimisations activées)
Elements: 100000, Iterations: 144
doAppend: 74.0042ms
doReserve: 27.0015ms
doAllocate: 5.0003ms
C++ debug produit:
Elements: 100000, Iterations: 144
doAppend: 2166.12ms
doReserve: 2082.12ms
doAllocate: 273.016ms
Le point ici est que, avec Python, vous pouvez obtenir une de 7 à 8% d'amélioration de la performance, et si vous pensez que vous êtes en train de rédiger une haute performance de l'application (ou si vous écrivez quelque chose qui est utilisé dans un service web ou quelque chose), ce n'est pas être sous-estimées, mais vous devrez peut-être repenser votre choix de langue.
Aussi, le code Python ici n'est pas vraiment de code Python. De commutation pour vraiment Pythonesque code ici donne de meilleures performances:
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)
Ce qui donne
Elements: 100000, Iterations: 144
doAppend: 2153.122902ms
doAllocate: 1346.076965ms
doGenerator: 1614.092112ms
(en 32 bits doGenerator fait mieux que doAllocate).
Ici, l'écart entre doAppend et doAllocate est nettement plus grande.
De toute évidence, les différences vraiment s'appliquent uniquement si vous faites cela plus qu'une poignée de fois, ou si vous faites cela sur un système fortement chargé où ces chiffres vont obtenir à l'échelle de plusieurs ordres de grandeur, ou si vous êtes aux prises avec de beaucoup plus grandes listes.
Le point ici: Faire de la pythonic façon pour les meilleures performances.
Mais si vous vous faites du souci à propos de général, des performances de haut niveau, le Python est la mauvaise langue. Le problème le plus fondamental étant que Python appels de fonction a toujours été jusqu'à 300x plus lent que les autres langues en raison de Python fonctionnalités comme les décorateurs etc (https://wiki.python.org/moin/PythonSpeed/PerformanceTips#Data_Aggregation#Data_Aggregation).