Risposte:
Per ottenere una copia completamente indipendente di un oggetto è possibile utilizzare la copy.deepcopy()funzione.
Per maggiori dettagli sulla copia superficiale e profonda, fare riferimento alle altre risposte a questa domanda e alla bella spiegazione in questa risposta a una domanda correlata .
Come posso creare una copia di un oggetto in Python?
Quindi, se cambio i valori dei campi del nuovo oggetto, l'oggetto vecchio non dovrebbe esserne influenzato.
Intendi un oggetto mutabile allora.
In Python 3, gli elenchi ottengono un copymetodo (in 2, useresti una sezione per fare una copia):
>>> a_list = list('abc')
>>> a_copy_of_a_list = a_list.copy()
>>> a_copy_of_a_list is a_list
False
>>> a_copy_of_a_list == a_list
True
Le copie superficiali sono solo copie del contenitore più esterno.
list.copy è una copia superficiale:
>>> list_of_dict_of_set = [{'foo': set('abc')}]
>>> lodos_copy = list_of_dict_of_set.copy()
>>> lodos_copy[0]['foo'].pop()
'c'
>>> lodos_copy
[{'foo': {'b', 'a'}}]
>>> list_of_dict_of_set
[{'foo': {'b', 'a'}}]
Non ottieni una copia degli oggetti interni. Sono lo stesso oggetto - quindi quando sono mutati, il cambiamento si presenta in entrambi i contenitori.
Le copie profonde sono copie ricorsive di ciascun oggetto interno.
>>> lodos_deep_copy = copy.deepcopy(list_of_dict_of_set)
>>> lodos_deep_copy[0]['foo'].add('c')
>>> lodos_deep_copy
[{'foo': {'c', 'b', 'a'}}]
>>> list_of_dict_of_set
[{'foo': {'b', 'a'}}]
Le modifiche non si riflettono nell'originale, ma solo nella copia.
Gli oggetti immutabili di solito non devono essere copiati. In effetti, se ci provi, Python ti darà semplicemente l'oggetto originale:
>>> a_tuple = tuple('abc')
>>> tuple_copy_attempt = a_tuple.copy()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'copy'
Le tuple non hanno nemmeno un metodo di copia, quindi proviamo con una sezione:
>>> tuple_copy_attempt = a_tuple[:]
Ma vediamo che è lo stesso oggetto:
>>> tuple_copy_attempt is a_tuple
True
Allo stesso modo per le stringhe:
>>> s = 'abc'
>>> s0 = s[:]
>>> s == s0
True
>>> s is s0
True
e per frozenset, anche se hanno un copymetodo:
>>> a_frozenset = frozenset('abc')
>>> frozenset_copy_attempt = a_frozenset.copy()
>>> frozenset_copy_attempt is a_frozenset
True
Gli oggetti immutabili devono essere copiati se è necessario copiare un oggetto interno mutabile.
>>> tuple_of_list = [],
>>> copy_of_tuple_of_list = tuple_of_list[:]
>>> copy_of_tuple_of_list[0].append('a')
>>> copy_of_tuple_of_list
(['a'],)
>>> tuple_of_list
(['a'],)
>>> deepcopy_of_tuple_of_list = copy.deepcopy(tuple_of_list)
>>> deepcopy_of_tuple_of_list[0].append('b')
>>> deepcopy_of_tuple_of_list
(['a', 'b'],)
>>> tuple_of_list
(['a'],)
Come possiamo vedere, quando l'oggetto interno della copia è mutato, l'originale non cambia.
Gli oggetti personalizzati di solito memorizzano i dati in un __dict__attributo o in __slots__(una struttura di memoria simile a una tupla).
Per creare un oggetto copiabile, definire __copy__(per copie superficiali) e / o __deepcopy__(per copie profonde).
from copy import copy, deepcopy
class Copyable:
__slots__ = 'a', '__dict__'
def __init__(self, a, b):
self.a, self.b = a, b
def __copy__(self):
return type(self)(self.a, self.b)
def __deepcopy__(self, memo): # memo is a dict of id's to copies
id_self = id(self) # memoization avoids unnecesary recursion
_copy = memo.get(id_self)
if _copy is None:
_copy = type(self)(
deepcopy(self.a, memo),
deepcopy(self.b, memo))
memo[id_self] = _copy
return _copy
Si noti che deepcopymantiene un dizionario di memoization di id(original)(o numeri di identità) alle copie. Per godere del buon comportamento con le strutture di dati ricorsive, assicurati di non averne già fatto una copia e, se lo hai, restituiscilo.
Quindi facciamo un oggetto:
>>> c1 = Copyable(1, [2])
E copyfa una copia superficiale:
>>> c2 = copy(c1)
>>> c1 is c2
False
>>> c2.b.append(3)
>>> c1.b
[2, 3]
E deepcopyora fa una copia profonda:
>>> c3 = deepcopy(c1)
>>> c3.b.append(4)
>>> c1.b
[2, 3]
Copia superficiale con copy.copy()
#!/usr/bin/env python3
import copy
class C():
def __init__(self):
self.x = [1]
self.y = [2]
# It copies.
c = C()
d = copy.copy(c)
d.x = [3]
assert c.x == [1]
assert d.x == [3]
# It's shallow.
c = C()
d = copy.copy(c)
d.x[0] = 3
assert c.x == [3]
assert d.x == [3]
Copia profonda con copy.deepcopy()
#!/usr/bin/env python3
import copy
class C():
def __init__(self):
self.x = [1]
self.y = [2]
c = C()
d = copy.deepcopy(c)
d.x[0] = 3
assert c.x == [1]
assert d.x == [3]
Documentazione: https://docs.python.org/3/library/copy.html
Testato su Python 3.6.5.
Credo che quanto segue dovrebbe funzionare con molti ben educati classificati in Python:
def copy(obj):
return type(obj)(obj)
(Certo, non sto parlando di "copie profonde", che è una storia diversa e che potrebbe non essere un concetto molto chiaro: quanto è profonda abbastanza?)
Secondo i miei test con Python 3, per oggetti immutabili, come tuple o stringhe, restituisce lo stesso oggetto (perché non è necessario creare una copia superficiale di un oggetto immutabile), ma per elenchi o dizionari crea una copia superficiale indipendente .
Naturalmente questo metodo funziona solo per le classi i cui costruttori si comportano di conseguenza. Casi d'uso possibili: creazione di una copia superficiale di una classe contenitore Python standard.
__init__metodo. Quindi, ho pensato che questo metodo potrebbe essere abbastanza buono per determinati scopi. In ogni caso, sarò interessato a commenti informativi su questo suggerimento.
class Foo(object): def __init__(self, arg): super(Foo, self).__init__() self.arg = argBasic come si arriva. Se lo faccio foo = Foo(3) bar = copy(foo) print(foo.arg) # 3 print(bar.arg) # <__main__.Foo object at ...>significa che la tua copyfunzione è interrotta anche per le classi più elementari. Ancora una volta, è un trucco accurato (quindi niente DV), ma non una risposta.
copy.copymetodo per fare copie superficiali, ma, forse ingenuamente, mi sembra che dovrebbe essere responsabilità della classe fornire un "costruttore di copie superficiali". In tal caso, perché non fornire lo stesso tipo di interfaccia dicte listfare? Quindi, se la tua classe vuole assumersi la responsabilità di copiare i suoi oggetti, perché non aggiungere una if isinstance(arg, type(self))clausola __init__?