Eu entendo a diferença entre copy
e deepcopy
no módulo de cópia. Usei copy.copy
e copy.deepcopy
antes com sucesso, mas esta é a primeira vez que realmente estou sobrecarregando os métodos __copy__
e __deepcopy__
. Eu já pesquisei ao redor e olhou através do built-in módulos Python para procurar instâncias do __copy__
e __deepcopy__
funções (por exemplo sets.py
, decimal.py
e fractions.py
), mas eu ainda não estou 100% de certeza que eu entendi direito.
Aqui está o meu cenário:
Eu tenho um objeto de configuração. Inicialmente, vou instanciar um objeto de configuração com um conjunto padrão de valores. Esta configuração será entregue a vários outros objetos (para garantir que todos os objetos comecem com a mesma configuração). No entanto, uma vez que a interação do usuário começa, cada objeto precisa ajustar suas configurações independentemente, sem afetar as configurações uns dos outros (o que me diz que vou precisar fazer cópias profundas de minha configuração inicial para entregar).
Aqui está um objeto de amostra:
class ChartConfig(object):
def __init__(self):
#Drawing properties (Booleans/strings)
self.antialiased = None
self.plot_style = None
self.plot_title = None
self.autoscale = None
#X axis properties (strings/ints)
self.xaxis_title = None
self.xaxis_tick_rotation = None
self.xaxis_tick_align = None
#Y axis properties (strings/ints)
self.yaxis_title = None
self.yaxis_tick_rotation = None
self.yaxis_tick_align = None
#A list of non-primitive objects
self.trace_configs = []
def __copy__(self):
pass
def __deepcopy__(self, memo):
pass
Qual é a maneira correta de implementar os métodos copy
e deepcopy
neste objeto para garantir copy.copy
e copy.deepcopy
fornecer o comportamento adequado?
fonte
Respostas:
As recomendações para personalização estão no final da página de documentos :
Uma vez que você parece não se importar com a personalização de decapagem, definir
__copy__
e__deepcopy__
definitivamente parece ser o caminho certo para você.Especificamente,
__copy__
(a cópia superficial) é muito fácil no seu caso ...:__deepcopy__
seria semelhante (aceitando ummemo
argumento também), mas antes do retorno ele teria que chamarself.foo = deepcopy(self.foo, memo)
qualquer atributoself.foo
que precise de uma cópia profunda (essencialmente atributos que são contêineres - listas, dicts, objetos não primitivos que contêm outras coisas através de seus__dict__
s).fonte
__copy__
/__deepcopy__
.self.foo = deepcopy(self.foo, memo)
...? Você realmente não quer dizernewone.foo = ...
?__init__
. Não é isso que a cópia faz. Além disso, muitas vezes há um caso de uso em que a decapagem e a cópia precisam ser diferentes. Na verdade, nem sei por que o copy tenta usar o protocolo de decapagem por padrão. Copiar é para manipulação na memória, decapagem é para persistência entre épocas; são coisas completamente diferentes que têm pouca relação entre si.Juntando a resposta de Alex Martelli e o comentário de Rob Young, você obtém o seguinte código:
estampas
aqui
__deepcopy__
preenche omemo
dicionário para evitar cópias em excesso, caso o próprio objeto seja referenciado a partir de seu membro.fonte
Transporter
?Transporter
é o nome da minha classe que estou escrevendo. Para essa classe, desejo substituir o comportamento de deepcopy.Transporter
?__deepcopy__
deveria incluir um teste para evitar recursão infinita: <! - language: lang-python -> d = id (self) result = memo.get (d, None) se o resultado não for Nenhum: retornar resultadomemo[id(self)]
realmente se acostuma para evitar recursão infinita. Reuni um pequeno exemplo que sugere quecopy.deepcopy()
internamente aborta a chamada para um objeto seid()
for uma chave dememo
, correto? Também é importante notar quedeepcopy()
parece fazer isso sozinho por padrão , o que torna difícil imaginar um caso em que definir__deepcopy__
manualmente é realmente necessário ...Seguindo a excelente resposta de Peter , para implementar uma deepcopy customizada, com alterações mínimas na implementação padrão (por exemplo, apenas modificando um campo como eu precisava):
fonte
delattr(self, '__deepcopy__')
entãosetattr(self, '__deepcopy__', deepcopy_method)
?Não está claro para o seu problema por que você precisa sobrescrever esses métodos, uma vez que você não deseja fazer nenhuma customização nos métodos de cópia.
De qualquer forma, se você quiser personalizar a cópia profunda (por exemplo, compartilhando alguns atributos e copiando outros), aqui está uma solução:
fonte
__deepcopy__
redefinir o método, pois terá__deepcopy__
= Nenhum?__deepcopy__
método não for encontrado (ouobj.__deepcopy__
retornar Nenhum), entãodeepcopy
recorre à função de cópia profunda padrão. Isso pode ser visto aqui__deepcopy__=None
atributo falso do clone. Veja o novo código.Eu posso estar um pouco fora dos detalhes, mas aqui vai;
Dos
copy
documentos ;Em outras palavras:
copy()
irá copiar apenas o elemento superior e deixar o resto como ponteiros na estrutura original.deepcopy()
irá copiar recursivamente sobre tudo.Ou seja,
deepcopy()
é o que você precisa.Se você precisa fazer algo realmente específico, pode substituir
__copy__()
ou__deepcopy__()
, conforme descrito no manual. Pessoalmente, eu provavelmente implementaria uma função simples (por exemplo,config.copy_config()
ou algo semelhante) para deixar claro que esse não é o comportamento padrão do Python.fonte
__copy__(
) e__deepcopy__()
. docs.python.org/library/copy.htmlO
copy
módulo usa eventualmente o protocolo__getstate__()
/ pickling , portanto, esses também são alvos válidos para serem substituídos.__setstate__()
A implementação padrão apenas retorna e define o
__dict__
da classe, então você não precisa chamarsuper()
e se preocupar com o truque inteligente de Eino Gourdin, acima .fonte
Com base na resposta clara de Antony Hatchkins, aqui está minha versão em que a classe em questão deriva de outra classe personalizada (st precisamos chamar
super
):fonte