Bonjour,
Suite à ce sujet pourriez vous donner votre avis (et vos explications) ?
Merci
Bonjour,
Suite à ce sujet pourriez vous donner votre avis (et vos explications) ?
Merci
Ou encore
?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 import time dico = {} liste = [] for i in range(100000): dico[i] = i tps1 = time.clock() for elem in dico: liste.append(dico[elem]) tps2 = time.clock() print(tps2 - tps1) del(liste[:]) tps1 = time.clock() for key, elt in dico.items(): liste.append(elt) tps2 = time.clock() print(tps2 - tps1)
Edit : liste = [] > del.
salut,
pour ce cas de figure particulier, j'utilise la méthode iteritems:
qui a les même perfo que l'itérateur générique du dictionnaire.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 tps1 = time.clock() for key, elt in dico.iteritems(): liste.append(elt) tps2 = time.clock()
items est à proscrire selon moi dans ce cas (itération) car il y a copie de l'information. mais il me semble que items était destiné à devenir une fonction génératrice (en lieu et place de iteritems). du coup ça dépend peut être des versions de Python.
J'ai fait les tests sur ma machine sous Windows Seven 64bit,
Avec des versions de Python 32bits :
2.5 :
méthode 1 : 0.0182519445094
méthode 2 : 0.0428708106773
2.6 :
méthode 1 : 0.0140861948377
méthode 2 : 0.040826728089
2.7 :
méthode 1 : 0.0132028070224
méthode 2 : 0.0315123637806
3.2 :
méthode 1 : 0.013682909095937069
méthode 2 : 0.012987721293448757
ben voilà, ça confirme, items est une génératrice en python 3 visiblement![]()
En py3, keys()/values()/items() retournent des vues (views), qui sont un nouveau type d’objets itérables, qui n’entraînent pas de copie des éléments du dict.
En py2, comme l’a dit kango, il faut systématiquement utiliser iteritems (ou iterkeys/itervalues) dans les boucles (sauf cas particuliers, évidemment), pour éviter cette copie…
C'est logique que items (ou iteritems en 2.x) soit plus rapide; il n'a pas besoin de faire de recherche dans la table de hashage. J'ai été voir les sources pour m'en assurer (on ne sait jamais comment c'est implémenté); c'est la fonction dictiter_iternextitem dans Objects/dictobject.c.
Mais en pratique, on voit que le gain n'est pas énorme, sauf cas pathologique (beaucoup de collisions dans la table de hashage):
méthode 1 (iteration+lookup): 2.5869514031419385
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 import time class BadInt(int): def __hash__(self): return 42 dico={} liste=[] for i in range(10000): d[BadInt(i)] = i [suite du script de PauseKawa]
méthode 2 (items): 0.0031533071874036978
(j'ai diminué le nombre d'itérations car j'ai perdu patience après 5 minutes avec 100000)
Au passage, on apprend à faire des trucs tordus en lisant les sources
Code Objects/dictobject.c : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 static PyDictEntry * lookdict(PyDictObject *mp, PyObject *key, register Py_hash_t hash) { [...] /* The compare did major nasty stuff to the * dict: start over. * XXX A clever adversary could prevent this * XXX from terminating. */ return lookdict(mp, key, hash);
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 >>> def doom_key(d, key): ... class EvilKey: ... def __hash__(self): ... return hash(key) ... def __eq__(self, other): ... d[key] = None # major nasty stuff ... return 0 ... def __str__(self): ... return str(key) ... d[EvilKey()] = None >>> d={} >>> doom_key(d,'test') >>> for x in d: print(x) ... test >>> d['test'] [...] RuntimeError: maximum recursion depth exceeded while calling a Python object >>> d['test'] = None [...] RuntimeError: maximum recursion depth exceeded while calling a Python object >>> del d['test'] [...] RuntimeError: maximum recursion depth exceeded while calling a Python object >>> d.pop('test') [...] RuntimeError: maximum recursion depth exceeded while calling a Python object >>> d.popitem() (<__main__.EvilKey object at 0x070AAE10>, None) >>> d {} # quand-même !
Bonjour,
A vrais dire j'étais partis sur ce genre de réflexion et c'est bien l'absence d'iteritem en Python 3 qui me fait préférer for item in dico. Histoire d'avoir du code compatible.
J'avais aussi penser à iter(dico), compatible lui, mais comme cela ne doit pas faire une grande différence autant rester lisible.
Pour conclure
items():
Une quasi égalité avec for item in dico en Python 3 mais deux fois plus lent en Python 2.
iteritems():
Légèrement plus rapide que for item in dico (et égalité avec iter(dico)) mais absent en Python 3.
Donc autant rester simple : for item in dico.
Merci à vous
@+
Partager