Como posso criar uma cópia de um objeto em Python?
Portanto, se eu alterar os valores dos campos do novo objeto, o objeto antigo não deverá ser afetado por isso.
Você quer dizer um objeto mutável então.
No Python 3, as listas obtêm um copy
método (em 2, você usaria uma fatia para fazer uma cópia):
>>> 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
Cópias rasas
Cópias rasas são apenas cópias do recipiente mais externo.
list.copy
é uma cópia superficial:
>>> 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'}}]
Você não recebe uma cópia dos objetos internos. Eles são o mesmo objeto - portanto, quando são mutados, a alteração aparece nos dois contêineres.
Cópias profundas
Cópias profundas são cópias recursivas de cada objeto interior.
>>> 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'}}]
As alterações não são refletidas no original, apenas na cópia.
Objetos imutáveis
Objetos imutáveis geralmente não precisam ser copiados. De fato, se você tentar, o Python fornecerá apenas o objeto original:
>>> 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'
As tuplas nem sequer têm um método de cópia, então vamos tentar com uma fatia:
>>> tuple_copy_attempt = a_tuple[:]
Mas vemos que é o mesmo objeto:
>>> tuple_copy_attempt is a_tuple
True
Da mesma forma para seqüências de caracteres:
>>> s = 'abc'
>>> s0 = s[:]
>>> s == s0
True
>>> s is s0
True
e para frozensets, mesmo que eles tenham um copy
método:
>>> a_frozenset = frozenset('abc')
>>> frozenset_copy_attempt = a_frozenset.copy()
>>> frozenset_copy_attempt is a_frozenset
True
Quando copiar objetos imutáveis
Objetos imutáveis devem ser copiados se você precisar de um objeto interior mutável copiado.
>>> 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'],)
Como podemos ver, quando o objeto interior da cópia é alterado , o original não muda.
Objetos personalizados
Objetos personalizados geralmente armazenam dados em um __dict__
atributo ou em __slots__
(uma estrutura de memória semelhante a uma tupla).
Para criar um objeto copiável, defina __copy__
(para cópias rasas) e / ou __deepcopy__
(para cópias profundas).
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
Observe que deepcopy
mantém um dicionário de memorização id(original)
(ou números de identidade) para cópias. Para ter bom comportamento com estruturas de dados recursivas, verifique se você ainda não fez uma cópia e, se tiver, devolva-a.
Então, vamos criar um objeto:
>>> c1 = Copyable(1, [2])
E copy
faz uma cópia superficial:
>>> c2 = copy(c1)
>>> c1 is c2
False
>>> c2.b.append(3)
>>> c1.b
[2, 3]
E deepcopy
agora faz uma cópia profunda:
>>> c3 = deepcopy(c1)
>>> c3.b.append(4)
>>> c1.b
[2, 3]