Question

J'ai une arborescence de widgets, par exemple. collection contient des modèles et modèle contient des widgets. Je souhaite copier toute la collection. copy.deepcopy est plus rapide que "pickle" et "pickle", mais cPickle, en écriture en C, est beaucoup plus rapide.

  1. Pourquoi ne devrais-je pas (toujours) utiliser cPickle au lieu de deepcopy?
  2. Existe-t-il une autre alternative de copie? parce que pickle est plus lent que deepcopy mais que cPickle est plus rapide, une implémentation C de deepcopy sera donc le gagnant

Exemple de code de test:

import copy
import pickle
import cPickle

class A(object): pass

d = {}
for i in range(1000):
    d[i] = A()

def copy1():
    return copy.deepcopy(d)

def copy2():
    return pickle.loads(pickle.dumps(d, -1))

def copy3():
    return cPickle.loads(cPickle.dumps(d, -1))

Délais:

>python -m timeit -s "import c" "c.copy1()"
10 loops, best of 3: 46.3 msec per loop

>python -m timeit -s "import c" "c.copy2()"
10 loops, best of 3: 93.3 msec per loop

>python -m timeit -s "import c" "c.copy3()"
100 loops, best of 3: 17.1 msec per loop
Était-ce utile?

La solution

Le problème, c’est que pickle + unpickle peut être plus rapide (dans l’implémentation C) car il est moins général que deepcopy: de nombreux objets peuvent être copiés en profondeur mais pas décapés. Supposons par exemple que votre classe A soit remplacée par ...:

class A(object):
  class B(object): pass
  def __init__(self): self.b = self.B()

maintenant, copy1 fonctionne toujours bien (la complexité de A le ralentit mais ne l'arrête absolument pas); copy2 et copy3 cassent, la fin de la trace de pile indique ...:

  File "./c.py", line 20, in copy3
    return cPickle.loads(cPickle.dumps(d, -1))
PicklingError: Can't pickle <class 'c.B'>: attribute lookup c.B failed

C'est-à-dire que le décapage suppose toujours que les classes et les fonctions sont des entités de niveau supérieur dans leurs modules, et les décape donc "par leur nom". - la copie en profondeur ne fait absolument pas de telles hypothèses.

Donc, si vous vous trouvez dans une situation où la vitesse de "copie en profondeur", est absolument crucial, chaque milliseconde compte, ET vous souhaitez tirer parti des limitations spéciales que vous SAVEZ s'appliquent aux objets que vous dupliquez, tels que ceux qui rendent le décapage applicable, ou ceux qui favorisent d'autres formes encore de sérialisation et autres raccourcis, en: tous les moyens sont bons, mais si vous le faites, vous DEVEZ savoir que votre système est contraint de respecter ces limitations à tout jamais et documenter cette décision de conception de manière très claire et explicite pour le bénéfice des futurs responsables.

Pour NORMAL, où vous voulez la généralité, utilisez deepcopy ! -)

Autres conseils

Vous devriez utiliser deepcopy car cela rend votre code plus lisible. L'utilisation d'un mécanisme de sérialisation pour copier des objets en mémoire est pour le moins déroutante pour un autre développeur qui lit votre code. L’utilisation de deepcopy signifie également que vous pouvez profiter des avantages des optimisations futures dans deepcopy.

Première règle d'optimisation: ne le faites pas.

Ce n'est pas que cPickle est toujours plus rapide que deepcopy (). CPickle est probablement toujours plus rapide que pickle, mais sa rapidité dépend de la

  • la taille et le niveau d'imbrication des structures à copier,
  • le type des objets contenus et
  • la taille de la représentation de chaîne décapée.

Si quelque chose peut être traité, il peut évidemment être copié en profondeur, mais le contraire ne se produit pas: Pour pouvoir conserver un produit, il faut qu'il soit entièrement sérialisé ; ce n'est pas le cas pour la copie en profondeur. En particulier, vous pouvez implémenter __ deepcopy __ de manière très efficace en copiant une structure en mémoire (pensez aux types d’extension), sans pouvoir tout enregistrer sur le disque. (Pensez à suspendre sur RAM plutôt que suspendre sur disque.)

Un type d'extension connu qui remplit les conditions ci-dessus peut être ndarray et constitue en fait un bon contre-exemple à votre observation: avec d = numpy.arange (100000000) , votre code donne différentes exécutions:

In [1]: import copy, pickle, cPickle, numpy

In [2]: d = numpy.arange(100000000)

In [3]: %timeit pickle.loads(pickle.dumps(d, -1))
1 loops, best of 3: 2.95 s per loop

In [4]: %timeit cPickle.loads(cPickle.dumps(d, -1))
1 loops, best of 3: 2.37 s per loop

In [5]: %timeit copy.deepcopy(d)
1 loops, best of 3: 459 ms per loop

Si __ deepcopy __ n'est pas implémenté, copy et pickle partagent une infrastructure commune (cf. module copy_reg , discuté Relation entre le cornichon et la copie profonde ).

Encore plus rapidement serait d'éviter la copie en premier lieu. Vous mentionnez que vous effectuez un rendu. Pourquoi faut-il copier des objets?

Court et un peu tardif:

  • Si vous devez quand même cPickler un objet, vous pouvez également utiliser la méthode cPickle pour la copie en profondeur (mais le document)

par exemple. Vous pourriez envisager:

def mydeepcopy(obj):
    try:
       return cPickle.loads(cPickle.dumps(obj, -1))
    except PicklingError:
       return deepcopy(obj)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top