copy - Duplica oggetti

Scopo Fornisce funzioni per la duplicazione di oggetti usando semantiche di deep e shallow copy
Versione Python 1.4

Il modulo copy include due funzioni, copy() e deepcopy() per la duplicazione di oggetti esistenti.

Copie shallow (vuote)

La copia shallow creata da copy() è un nuovo contenitore riempito con i riferimenti al contenuto dell'oggetto originale. Ad esempio una nuova lista viene costruita e ad essa sono aggiunti gli elementi della lista originale.

import copy

class MyClass:
    def __init__(self, name):
        self.name = name
    def __cmp__(self, other):
        return cmp(self.name, other.name)

a = MyClass('a')
l = [ a ]
dup = copy.copy(l)

print 'l  :', l
print 'dup:', dup
print 'dup è l:', (dup is l)
print 'dup == l:', (dup == l)
print 'dup[0] è l[0]:', (dup[0] is l[0])
print 'dup[0] == l[0]:', (dup[0] == l[0])

Per una copia shallow l'istanza di MyClass non viene duplicata quindi il riferimento nella lista di dup è lo stesso oggetto che si trova nella lista l.

$ python copy_shallow.py
l  : [<__main__.MyClass instance at 0x8c648>]
dup: [<__main__.MyClass instance at 0x8c648>]
dup è l: False
dup == l: True
dup[0] è l[0]: True
dup[0] == l[0]: True

Copie deep (profonde)

La copia deep creata da deepcopy() è un nuovo contenitore riempito con copie del contenuto dell'oggetto originale. Ad esempio viene costrutita una nuova lista e gli elementi della lista originale vengono copiati, quindi le copie sono aggiunte alla nuova lista.

Sostituendo la chiamata a copy() con quella a deepcopy() la differenza diventa evidente.

dup = copy.deepcopy(l)

Notare che il primo elemento della lista non è più un riferimento allo stesso oggetto, ma i due oggetti risultano comunque uguali.

$ python copy_deep.py
l  : [<__main__.MyClass instance at 0x8c670>]
dup: [<__main__.MyClass instance at 0x8c788>]
dup è l: False
dup == l: True
dup[0] è l[0]: False
dup[0] == l[0]: True

Controllare il comportamento della copia

E' possibilie controllare in che modo le copie sono effettuate tramite gli agganci di __copy__ e __deepcopy__.

  • __copy__() viene chiamato senza alcun parametro e dovrebbe restituire una copia shallow dell'oggetto
  • __deepcopy()__ viene chiamata con un dizionario memo, e dovrebbe restituire una copia deep dell'oggetto. Otni attributo dei membri che è oggetto di copia deep dovrebbe essere passato al copy.deepcopy(), assieme al dizionario memo, per controllare se esiste recursione (vedere sotto).

Questo esempio dimostra come vengono chiamati i metodi.

import copy

class MyClass:
    def __init__(self, name):
        self.name = name
    def __cmp__(self, other):
        return cmp(self.name, other.name)
    def __copy__(self):
        print '__copy__()'
        return MyClass(self.name)
    def __deepcopy__(self, memo):
        print '__deepcopy__(%s)' % str(memo)
        return MyClass(copy.deepcopy(self.name, memo))

a = MyClass('a')

sc = copy.copy(a)
dc = copy.deepcopy(a)
$ python copy_hooks.py
__copy__()
__deepcopy__({})

Recursione nella Deep Copy

Per evitare problemi con la duplicazione di strutture dati recursive, {sbk}deepcopy(){ebk} usa un dizionario per tenere traccia degli oggetti che sono già stati copiati. Questo dizionario viene passato al metodo {sbk}__deepcopy__(){ebk} così che possa essere da esso usato

Questo esempio mostra come una struttura dati interconnessa come un Digraph possa essere utilizzata come protezione contro la recursione implementando il metodo {sbk}__deepcopy__(){ebk}. Questo particolare esempio è solo a scopi illustrativi, visto che la implementazione predefinita di {sbk}deepcopy(){ebk} già gestisce correttamente i casi di recursione.

import copy
import pprint

class Graph:
    def __init__(self, name, connections):
        self.name = name
        self.connections = connections
    def addConnection(self, other):
        self.connections.append(other)
    def __repr__(self):
        return '<Graph(%s) id=%s>' % (self.name, id(self))
    def __deepcopy__(self, memo):
        print
        print repr(self)
        not_there = []
        existing = memo.get(self, not_there)
        if existing is not not_there:
            print '  GIA\' COPIATO IN', repr(existing)
            return existing
        pprint.pprint(memo, indent=4, width=40)
        dup = Graph(copy.deepcopy(self.name, memo), [])
        print '  COPIA VERSO', repr(dup)
        memo[self] = dup
        for c in self.connections:
            dup.addConnection(copy.deepcopy(c, memo))
        return dup

root = Graph('root', [])
a = Graph('a', [root])
b = Graph('b', [a, root])
root.addConnection(a)
root.addConnection(b)

dup = copy.deepcopy(root)

Per prima cosa alcuni metodi base di graph. graph può essere inizializzato con un nome ed una lista di nodi esistenti ai quali è connesso. Il metodo addConnection() viene usato per impostare connessioni bidirezionali. Viene anche usato dall'operatore di deepcopy.

Il metodo {sbk}__deepcopy__() stampa messaggi per mostrare come è stato chiamato, e gestisce i contenuti del dizionario memo come richiesto. Invece di copiare brutalmente la lista di connessione, crea una nuova lista ed aggiunge le copie delle connessioni individuali ad essa. Questo assicura che il dizionario memo sia aggiornato e che ogni nodo venga duplicato, evitando problemi di recursione o copie aggiuntive dei nodi. Come in precedenza, restituisce l'oggetto copiato quando ha finito.

Successivamente possiamo impostare un graph con nodi radice, a, b. Gli estremi sono a->root, b->a, b->root, root->, root->b

Quando il nodo root viene copiato osserviamo:

$ python copy_recursion.py

<Graph(root) id=580624>
{   }
  COPYING TO <Graph(root) id=804104>

<Graph(a) id=803784>
{   <Graph(root) id=580624>: <Graph(root) id=804104>,
    565472: 'root',
    571120: ['root']}
  COPIA VERSO <Graph(a) id=804064>

<Graph(root) id=580624>
  GIA' COPIATI IN <Graph(root) id=804104>

<Graph(b) id=803824>
{   <Graph(root) id=580624>: <Graph(root) id=804104>,
    <Graph(a) id=803784>: <Graph(a) id=804064>,
    290848: 'a',
    565472: 'root',
    571120: [   'root',
                'a',
                <Graph(root) id=580624>,
                <Graph(a) id=803784>],
    580624: <Graph(root) id=804104>,
    803784: <Graph(a) id=804064>}
  COPIA VERSO <Graph(b) id=807360>

Notare che la seconda volta che viene esaminato il nodo root, mentre il nodo a viene copiato, la recursione viene riconosciuta e viene usata la copia esistente in luogo di una nuova.

Vedere anche:

copy
La documentazione della libreria standard per questo modulo.
struct
Il modulo struct
Numerical Python
NumPy è una libreria Python per lavorare con efficacia con grandi insiemi di dati
Strutture di dati in memoria