Domanda

Ho una struttura ad albero di widget, ad es. la raccolta contiene modelli e il modello contiene widget. Voglio copiare l'intera collezione, copy.deepcopy è più veloce rispetto a "decapare e decapare" l'oggetto ma cPickle come scritto in C è molto più veloce, quindi

  1. Perché non dovremmo sempre usare cPickle invece di deepcopy?
  2. Esistono altre alternative alla copia? perché il sottaceto è più lento del deepcopy ma cPickle è più veloce, quindi potrebbe essere un'implementazione in C del deepcopy che sarà il vincitore

Codice di prova di esempio:

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))

Tempi:

>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
È stato utile?

Soluzione

Il problema è che pickle + unpickle può essere più veloce (nell'implementazione in C) perché è meno generale rispetto al deepcopy: molti oggetti possono essere copiati in profondità ma non decapati. Supponiamo ad esempio che la tua classe A sia stata modificata in ...:

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

ora copy1 funziona ancora bene (la complessità di A rallenta ma non la ferma assolutamente); copy2 e copy3 si interrompono, la fine della traccia dello stack dice ...:

  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

Ad esempio, il decapaggio presuppone sempre che le classi e le funzioni siano entità di livello superiore nei loro moduli, e quindi le decapita "per nome". - il deepcopying non fa assolutamente simili assunzioni.

Quindi se hai una situazione in cui la velocità di "un po 'di copia profonda" è assolutamente cruciale, ogni millesimo di secondo è importante, e vuoi approfittare delle speciali limitazioni che SAPI applicare agli oggetti che stai duplicando, come quelli che rendono applicabile il decapaggio, o quelli che favoriscono altre forme ma di serializzazioni e altre scorciatoie, da tutti i mezzi vanno avanti, ma se lo fai DEVI essere consapevole del fatto che stai vincolando il tuo sistema a rispettare tali limiti per sempre, e documenta la decisione di progettazione in modo molto chiaro ed esplicito a beneficio dei futuri manutentori.

Per il caso NORMAL, dove si desidera la generalità, utilizzare deepcopy ! -)

Altri suggerimenti

Dovresti usare deepcopy perché rende il tuo codice più leggibile. L'uso di un meccanismo di serializzazione per copiare oggetti in memoria è per lo meno fonte di confusione per un altro sviluppatore che legge il tuo codice. L'uso di deepcopy significa anche ottenere i benefici delle future ottimizzazioni in deepcopy.

Prima regola di ottimizzazione: no.

non è sempre il caso che cPickle sia più veloce di deepcopy (). Mentre cPickle è probabilmente sempre più veloce di sottaceto, se è più veloce della deepcopy dipende da

  • le dimensioni e il livello di annidamento delle strutture da copiare,
  • il tipo di oggetti contenuti e
  • la dimensione della rappresentazione in stringa decapata.

Se qualcosa può essere decapato, può ovviamente essere copiato in profondità, ma non è il contrario: Per decapare qualcosa, deve essere completamente serializzato ; questo non è il caso della copia in profondità. In particolare, puoi implementare __deepcopy__ in modo molto efficiente copiando una struttura in memoria (pensa ai tipi di estensione), senza poter salvare tutto sul disco. (Pensa a suspend-to-RAM vs. suspend-to-disk.)

Un tipo di estensione noto che soddisfa le condizioni di cui sopra può essere ndarray e, in effetti, serve da valido controesempio alla tua osservazione: con d = numpy.arange (100000000) , il tuo codice offre diversi runtime:

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

Se __deepcopy__ non è implementato, copia e pickle condividono l'infrastruttura comune (cfr. modulo copy_reg , discusso in Relazione tra sottaceto e deepcopy ).

Ancora più veloce sarebbe evitare la copia in primo luogo. Dici che stai facendo il rendering. Perché deve copiare oggetti?

Breve e un po 'in ritardo:

  • Se devi comunque cPickle un oggetto, puoi anche usare il metodo cPickle per deepcopy (ma documento)

es. Potresti considerare:

def mydeepcopy(obj):
    try:
       return cPickle.loads(cPickle.dumps(obj, -1))
    except PicklingError:
       return deepcopy(obj)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top