Je comprends la différence entre copy
et deepcopy
dans le module de copie. J'ai utilisé copy.copy
et copy.deepcopy
avant avec succès, mais c'est la première fois que je surcharge les méthodes __copy__
et __deepcopy__
. Je l' ai déjà googlé autour et regardé à travers le haut-modules Python pour rechercher des instances du __copy__
et des __deepcopy__
fonctions (par exemple sets.py
, decimal.py
et fractions.py
), mais je ne suis pas encore à 100% sûr que je l' ai raison.
Voici mon scénario:
J'ai un objet de configuration. Au départ, je vais instancier un objet de configuration avec un ensemble de valeurs par défaut. Cette configuration sera transmise à plusieurs autres objets (pour garantir que tous les objets démarrent avec la même configuration). Cependant, une fois que l'interaction utilisateur commence, chaque objet doit modifier ses configurations indépendamment sans affecter les configurations de l'autre (ce qui me dit que je vais devoir faire des copies en profondeur de ma configuration initiale à remettre).
Voici un exemple d'objet:
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
Quelle est la bonne façon d'implémenter les méthodes copy
et deepcopy
sur cet objet pour m'assurer copy.copy
et copy.deepcopy
me donner le bon comportement?
la source
Réponses:
Les recommandations de personnalisation se trouvent à la toute fin de la page de documentation :
Puisque vous semblez ne pas vous soucier de la personnalisation du décapage, définir
__copy__
et__deepcopy__
semble définitivement être la bonne voie pour vous.Plus précisément,
__copy__
(la copie superficielle) est assez facile dans votre cas ...:__deepcopy__
serait similaire (accepter unmemo
argument aussi) mais avant le retour, il devrait appelerself.foo = deepcopy(self.foo, memo)
tout attributself.foo
nécessitant une copie approfondie (essentiellement des attributs qui sont des conteneurs - des listes, des dictionnaires, des objets non primitifs qui contiennent d'autres éléments à travers leurs__dict__
s).la source
__copy__
/__deepcopy__
.self.foo = deepcopy(self.foo, memo)
...? Tu ne veux pas vraiment direnewone.foo = ...
?__init__
. Ce n'est pas ce que fait la copie. Il existe également très souvent un cas d'utilisation où le décapage et la copie doivent être différents. En fait, je ne sais même pas pourquoi Copy essaie d'utiliser le protocole de décapage par défaut. La copie est pour la manipulation en mémoire, le décapage est pour la persistance entre les époques; ce sont des choses complètement différentes qui n'ont guère de rapport entre elles.En réunissant la réponse d'Alex Martelli et le commentaire de Rob Young, vous obtenez le code suivant:
impressions
ici
__deepcopy__
remplit lememo
dict pour éviter une copie excessive au cas où l'objet lui-même serait référencé à partir de son membre.la source
Transporter
?Transporter
est le nom de ma classe que j'écris. Pour cette classe, je souhaite remplacer le comportement de deepcopy.Transporter
?__deepcopy__
devrait inclure un test pour éviter la récursivité infinie: <! - language: lang-python -> d = id (self) result = memo.get (d, None) si result is not None: return resultmemo[id(self)]
s'habitue réellement à empêcher une récursivité infinie. J'ai rassemblé un court exemple qui suggèrecopy.deepcopy()
qu'interrompt en interne l'appel à un objet siid()
c'est une clé dememo
, correct? Il est également intéressant de noter que celadeepcopy()
semble faire cela seul par défaut , ce qui rend difficile d'imaginer un cas où la définition__deepcopy__
manuelle est réellement nécessaire ...Suite à l'excellente réponse de Peter , pour implémenter une copie profonde personnalisée, avec une modification minimale de l'implémentation par défaut (par exemple, simplement modifier un champ comme j'avais besoin):
la source
delattr(self, '__deepcopy__')
alorssetattr(self, '__deepcopy__', deepcopy_method)
?La raison pour laquelle vous devez remplacer ces méthodes ne ressort pas clairement de votre problème, car vous ne souhaitez pas personnaliser les méthodes de copie.
Quoi qu'il en soit, si vous souhaitez personnaliser la copie complète (par exemple en partageant certains attributs et en copiant d'autres), voici une solution:
la source
__deepcopy__
réinitialiser sa méthode car il aura__deepcopy__
= None?__deepcopy__
méthode n'est pas trouvée (ouobj.__deepcopy__
renvoie None), alorsdeepcopy
revient à la fonction standard de copie profonde. Cela peut être vu ici__deepcopy__=None
attribut du clone. Voir le nouveau code.Je pourrais être un peu en retard sur les détails, mais voilà;
À partir des
copy
documents ;En d'autres termes:
copy()
copiera uniquement l'élément supérieur et laissera le reste comme pointeurs dans la structure d'origine.deepcopy()
copiera récursivement sur tout.C'est
deepcopy()
ce dont vous avez besoin.Si vous avez besoin de faire quelque chose de vraiment spécifique, vous pouvez remplacer
__copy__()
ou__deepcopy__()
, comme décrit dans le manuel. Personnellement, j'implémenterais probablement une fonction simple (par exempleconfig.copy_config()
ou autre) pour indiquer clairement qu'il ne s'agit pas d'un comportement standard Python.la source
__copy__(
) et__deepcopy__()
. docs.python.org/library/copy.htmlLe
copy
module utilise finalement le protocole__getstate__()
/ pickling , ce sont donc également des cibles valides à remplacer.__setstate__()
L'implémentation par défaut renvoie et définit simplement le
__dict__
de la classe, vous n'avez donc pas à appelersuper()
et à vous soucier de l'astuce intelligente d'Eino Gourdin, ci - dessus .la source
En s'appuyant sur la réponse claire d'Antony Hatchkins, voici ma version où la classe en question dérive d'une autre classe personnalisée (st nous devons appeler
super
):la source