Un code comme celui-ci arrive souvent:
l = []
while foo:
#baz
l.append(bar)
#qux
C'est vraiment lent si vous êtes sur le point d'ajouter des milliers d'éléments à votre liste, car la liste devra être constamment redimensionnée pour s'adapter aux nouveaux éléments.
En Java, vous pouvez créer une ArrayList avec une capacité initiale. Si vous avez une idée de la taille de votre liste, ce sera beaucoup plus efficace.
Je comprends qu'un code comme celui-ci peut souvent être re-factorisé dans une compréhension de liste. Si la boucle for / while est très compliquée, cependant, c'est irréalisable. Y a-t-il un équivalent pour nous, programmeurs Python?
python
list
dictionary
initialization
Claudiu
la source
la source
Réponses:
Résultats . (évaluer chaque fonction 144 fois et faire la moyenne de la durée)
Conclusion . Cela n'a guère d'importance.
L'optimisation prématurée est la racine de tout Mal.
la source
Les listes Python n'ont pas de pré-allocation intégrée. Si vous avez vraiment besoin de faire une liste et que vous devez éviter la surcharge de l'ajout (et vous devez vérifier que c'est le cas), vous pouvez le faire:
Vous pourriez peut-être éviter la liste en utilisant un générateur à la place:
De cette façon, la liste n'est pas du tout stockée en mémoire, elle est simplement générée au besoin.
la source
Version courte: utilisation
pour pré-allouer une liste (c'est-à-dire pour pouvoir adresser des éléments de «taille» de la liste au lieu de former progressivement la liste en ajoutant). Cette opération est TRES rapide, même sur de grandes listes. L'allocation de nouveaux objets qui seront ultérieurement affectés à des éléments de liste prendra BEAUCOUP plus de temps et sera LE goulot d'étranglement de votre programme, en termes de performances.
Version longue:
Je pense que le temps d'initialisation doit être pris en compte. Comme en python, tout est une référence, peu importe que vous définissiez chaque élément sur None ou sur une chaîne - de toute façon, ce n'est qu'une référence. Cependant, cela prendra plus de temps si vous souhaitez créer un nouvel objet pour chaque élément à référencer.
Pour Python 3.2:
Évaluation:
Comme vous pouvez le voir, créer une grande liste de références au même objet None prend très peu de temps.
La prépension ou l'extension prend plus de temps (je n'ai rien fait en moyenne, mais après l'avoir exécuté plusieurs fois, je peux vous dire que l'extension et l'ajout prennent à peu près le même temps).
Allouer un nouvel objet à chaque élément - c'est ce qui prend le plus de temps. Et la réponse de S.Lott fait cela - formate une nouvelle chaîne à chaque fois. Ce qui n'est pas strictement obligatoire - si vous souhaitez pré-allouer de l'espace, créez simplement une liste de Aucun, puis attribuez des données aux éléments de la liste à volonté. Dans tous les cas, il faut plus de temps pour générer des données que pour ajouter / étendre une liste, que vous la génériez lors de la création de la liste ou après cela. Mais si vous voulez une liste peu peuplée, commencer par une liste de Aucun est certainement plus rapide.
la source
[]*
approcheLa manière pythonique pour cela est:
ou quelle que soit la valeur par défaut avec laquelle vous souhaitez pré-remplir, par exemple
[EDIT: Caveat Emptor La
[Beer()] * 99
syntaxe en crée unBeer
, puis remplit un tableau avec 99 références à la même instance]L'approche par défaut de Python peut être assez efficace, bien que cette efficacité diminue à mesure que vous augmentez le nombre d'éléments.
Comparer
avec
Sur mon Windows 7 i7, Python 64 bits donne
Alors que C ++ donne (construit avec MSVC, 64 bits, optimisations activées)
La génération de débogage C ++ produit:
Le point ici est qu'avec Python, vous pouvez obtenir une amélioration des performances de 7 à 8%, et si vous pensez que vous écrivez une application haute performance (ou si vous écrivez quelque chose qui est utilisé dans un service Web ou autre), alors ce n'est pas à renifler, mais vous devrez peut-être repenser votre choix de langue.
De plus, le code Python ici n'est pas vraiment du code Python. Le passage au code vraiment Pythonesque donne ici de meilleures performances:
Qui donne
(en 32 bits, doGenerator fait mieux que doAllocate).
Ici, l'écart entre doAppend et doAllocate est nettement plus grand.
De toute évidence, les différences ici ne s'appliquent vraiment que si vous faites cela plus d'une poignée de fois ou si vous le faites sur un système très chargé où ces nombres vont être réduits par ordre de grandeur, ou si vous avez affaire à listes considérablement plus grandes.
Le point ici: faites-le de manière pythonique pour la meilleure performance.
Mais si vous vous inquiétez des performances générales de haut niveau, Python n'est pas le bon langage. Le problème le plus fondamental est que les appels de fonction Python ont traditionnellement été jusqu'à 300 fois plus lents que les autres langages en raison des fonctionnalités Python telles que les décorateurs, etc. ( https://wiki.python.org/moin/PythonSpeed/PerformanceTips#Data_Aggregation#Data_Aggregation ).
la source
timeit
timeit
, que vous devez utiliser lors de la synchronisation de votre code Python; Je ne parle pas de C ++, évidemment.bottles = [Beer()] * 99
ne crée pas 99 objets Beer. Au lieu de cela, crée un objet Beer avec 99 références. Si vous le muter, tous les éléments de la liste seront mutés, cause(bottles[i] is bootles[j]) == True
pour chacuni != j. 0<= i, j <= 99
.Comme d'autres l'ont mentionné, le moyen le plus simple de pré-amorcer une liste avec
NoneType
objets.Cela étant dit, vous devez comprendre le fonctionnement réel des listes Python avant de décider que cela est nécessaire. Dans l'implémentation CPython d'une liste, le tableau sous-jacent est toujours créé avec une surcharge, dans des tailles de plus en plus grandes
( 4, 8, 16, 25, 35, 46, 58, 72, 88, 106, 126, 148, 173, 201, 233, 269, 309, 354, 405, 462, 526, 598, 679, 771, 874, 990, 1120, etc)
, de sorte que le redimensionnement de la liste ne se produit pas aussi souvent.En raison de ce comportement, la plupart des
list.append()
fonctions sontO(1)
complexes pour les appendices, ayant seulement une complexité accrue lors du franchissement d'une de ces limites, à quel point la complexité seraO(n)
. Ce comportement est ce qui conduit à l'augmentation minimale du temps d'exécution dans la réponse de S. Lott.Source: http://www.laurentluce.com/posts/python-list-implementation/
la source
J'ai exécuté le code de @ s.lott et produit la même augmentation de 10% des performances en pré-allouant. a essayé l'idée de @ jeremy en utilisant un générateur et a pu voir la performance de la génération mieux que celle du doAllocate. Pour mon projet, l'amélioration de 10% compte, alors merci à tout le monde car cela aide beaucoup.
la source
Des problèmes de pré-allocation en Python surviennent si vous travaillez avec numpy, qui a plus de tableaux de type C. Dans ce cas, les problèmes de pré-allocation concernent la forme des données et la valeur par défaut.
Considérez numpy si vous effectuez des calculs numériques sur des listes massives et que vous voulez des performances.
la source
Pour certaines applications, un dictionnaire peut être ce que vous recherchez. Par exemple, dans la méthode find_totient, j'ai trouvé plus pratique d'utiliser un dictionnaire car je n'avais pas d'index zéro.
Ce problème pourrait également être résolu avec une liste préallouée:
Je pense que ce n'est pas aussi élégant et sujet aux bugs parce que je stocke None qui pourrait lever une exception si je les utilise accidentellement de manière incorrecte, et parce que j'ai besoin de penser aux cas limites que la carte me permet d'éviter.
Il est vrai que le dictionnaire ne sera pas aussi efficace, mais comme d'autres l'ont commenté, de petites différences de vitesse ne valent pas toujours des risques de maintenance importants .
la source
D'après ce que je comprends, les listes Python sont déjà assez similaires aux ArrayLists. Mais si vous voulez modifier ces paramètres, j'ai trouvé cet article sur le net qui peut être intéressant (en gros, créez simplement votre propre
ScalableList
extension):http://mail.python.org/pipermail/python-list/2000-May/035082.html
la source