Entiendo la diferencia entre copy
vs. deepcopy
en el módulo de copia. He usado copy.copy
y copy.deepcopy
antes con éxito, pero esta es la primera vez que sobrecargo los métodos __copy__
y __deepcopy__
. Ya he buscado en Google y miré a través de la incorporada en los módulos de Python para buscar instancias de la __copy__
y __deepcopy__
funciones (por ejemplo sets.py
, decimal.py
y fractions.py
), pero todavía no estoy 100% seguro de que tengo derecho.
Aquí está mi escenario:
Tengo un objeto de configuración. Inicialmente, voy a crear una instancia de un objeto de configuración con un conjunto de valores predeterminado. Esta configuración se transferirá a varios otros objetos (para garantizar que todos los objetos comiencen con la misma configuración). Sin embargo, una vez que comienza la interacción del usuario, cada objeto debe modificar sus configuraciones de forma independiente sin afectar las configuraciones de los demás (lo que me dice que tendré que hacer copias en profundidad de mi configuración inicial para entregar).
Aquí hay un objeto de muestra:
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
¿Cuál es la forma correcta de implementar los métodos copy
y deepcopy
en este objeto para garantizar copy.copy
y copy.deepcopy
darme el comportamiento adecuado?
fuente
Respuestas:
Las recomendaciones para la personalización se encuentran al final de la página de documentos :
Dado que parece que no le importa la personalización del decapado, definir
__copy__
y__deepcopy__
definitivamente parece ser el camino correcto para usted.Específicamente,
__copy__
(la copia superficial) es bastante fácil en su caso ...:__deepcopy__
sería similar (aceptando unmemo
arg también) pero antes de la devolución tendría que solicitarself.foo = deepcopy(self.foo, memo)
cualquier atributoself.foo
que necesite copia profunda (esencialmente atributos que son contenedores: listas, dictados, objetos no primitivos que contienen otras cosas a través de sus__dict__
s).fuente
__copy__
/__deepcopy__
.self.foo = deepcopy(self.foo, memo)
...? ¿No te refieres realmentenewone.foo = ...
?__init__
. Eso no es lo que hace la copia. También hay muy a menudo un caso de uso en el que el decapado y la copia deben ser diferentes. De hecho, ni siquiera sé por qué copy intenta utilizar el protocolo de decapado de forma predeterminada. La copia es para la manipulación en memoria, el decapado es para la persistencia entre épocas; son cosas completamente diferentes que tienen poca relación entre sí.Al juntar la respuesta de Alex Martelli y el comentario de Rob Young, se obtiene el siguiente código:
huellas dactilares
aquí se completa
__deepcopy__
elmemo
dict para evitar el exceso de copias en caso de que el objeto en sí sea referenciado desde su miembro.fuente
Transporter
?Transporter
es el nombre de mi clase que estoy escribiendo. Para esa clase, quiero anular el comportamiento de la copia profunda.Transporter
?__deepcopy__
debería incluir una prueba para evitar la recursividad infinita: <! - language: lang-python -> d = id (self) result = memo.get (d, None) si el resultado no es None: return resultmemo[id(self)]
se acostumbra realmente para evitar la recursividad infinita. He reunido un breve ejemplo que sugiere quecopy.deepcopy()
internamente aborta la llamada a un objeto siid()
es una clave dememo
, ¿correcto? También vale la pena señalar quedeepcopy()
parece hacer esto por sí solo de forma predeterminada , lo que hace que sea difícil imaginar un caso en el que la definición__deepcopy__
manual sea realmente necesaria ...Siguiendo la excelente respuesta de Peter , para implementar una copia profunda personalizada, con una alteración mínima a la implementación predeterminada (por ejemplo, simplemente modificando un campo como yo necesitaba):
fuente
delattr(self, '__deepcopy__')
entoncessetattr(self, '__deepcopy__', deepcopy_method)
?Su problema no deja claro por qué necesita anular estos métodos, ya que no desea personalizar los métodos de copia.
De todos modos, si desea personalizar la copia profunda (por ejemplo, compartiendo algunos atributos y copiando otros), aquí tiene una solución:
fuente
__deepcopy__
reinicio de su método, ya que tendrá__deepcopy__
= Ninguno?__deepcopy__
no se encuentra el método (oobj.__deepcopy__
devuelve Ninguno),deepcopy
recurre a la función estándar de copia profunda. Esto se puede ver aquí__deepcopy__=None
atributo falso del clon. Ver nuevo código.Puede que esté un poco fuera de lugar en los detalles, pero aquí va;
De los
copy
documentos ;En otras palabras:
copy()
copiará solo el elemento superior y dejará el resto como punteros en la estructura original.deepcopy()
copiará de forma recursiva sobre todo.Es decir,
deepcopy()
es lo que necesitas.Si necesita hacer algo realmente específico, puede anular
__copy__()
o__deepcopy__()
, como se describe en el manual. Personalmente, probablemente implementaría una función simple (por ejemplo,config.copy_config()
o tal) para dejar claro que no es el comportamiento estándar de Python.fuente
__copy__(
) y__deepcopy__()
. docs.python.org/library/copy.htmlEl
copy
módulo usa eventualmente el protocolo__getstate__()
/ pickling , por lo que estos también son objetivos válidos para anular.__setstate__()
La implementación predeterminada simplemente regresa y establece el
__dict__
de la clase, por lo que no tiene que llamarsuper()
ni preocuparse por el ingenioso truco de Eino Gourdin, arriba .fuente
Sobre la base de la respuesta limpia de Antony Hatchkins, aquí está mi versión en la que la clase en cuestión deriva de otra clase personalizada (a la que debemos llamar
super
):fuente