127 votes

Copie du constructeur en python ?

Existe-t-il un constructeur de copie en python ? Si non, que dois-je faire pour réaliser quelque chose de similaire ?

J'utilise une bibliothèque et j'ai ajouté une fonctionnalité supplémentaire à l'une des classes qu'elle contient. Je veux pouvoir convertir les objets que je reçois de la bibliothèque en instances de ma propre classe.

0 votes

La question suivante pourrait vous intéresser [avertissement : c'est moi qui l'ai posée] : stackoverflow.com/questions/990758/

0 votes

Faites attention. Les avertissements affichés par certains des répondeurs ne sont pas à sous-estimer.

0 votes

Cela n'a pas l'air très lisible, je vais probablement changer mon code en utilisant l'héritage pour encapsuler l'autre objet à la place.

89voto

Tom Dunham Points 2466

Je pense que vous voulez le module de copie

import copy

x = copy.copy(y)        # make a shallow copy of y
x = copy.deepcopy(y)    # make a deep copy of y

vous pouvez contrôler la copie de la même manière que vous contrôlez cornichon .

14 votes

deepcopy fonctionne bien pour faire cela en dehors de la définition de la classe. Mais @Zitrax veut le faire à l'intérieur de sa définition de classe afin que la nouvelle instance hérite des attributs (données) d'un objet d'un type (classe) différent (parent).

46voto

qwerty9967 Points 198

En python, le constructeur de copie peut être défini en utilisant des arguments par défaut. Disons que vous voulez que le constructeur normal exécute la fonction non_copy_constructor(self) et le constructeur de la copie devrait s'exécuter copy_constructor(self, orig) . Vous pouvez alors procéder comme suit :

class Foo:
    def __init__(self, orig=None):
        if orig is None:
            self.non_copy_constructor()
        else:
            self.copy_constructor(orig)
    def non_copy_constructor(self):
        # do the non-copy constructor stuff
    def copy_constructor(self, orig):
        # do the copy constructor

a=Foo()  # this will call the non-copy constructor
b=Foo(a) # this will call the copy constructor

27voto

Godsmith Points 334

Un exemple simple de mon implémentation habituelle d'un constructeur de copie :

import copy

class Foo:

  def __init__(self, data):
    self._data = data

  @classmethod
  def from_foo(cls, class_instance):
    data = copy.deepcopy(class_instance._data) # if deepcopy is necessary
    return cls(data)

2 votes

Joli. Cela fonctionnerait comme suit Foo.from_foo(foo) . Un raffinement supplémentaire consisterait à faire en sorte que cela fonctionne pour Foo(foo) C'est ce que souhaite probablement le PO @Zitrax.

2 votes

Les raisons pour lesquelles je n'aime pas ça sont les suivantes : 1. Cela oblige __init__ pour prendre data comme entrée, et n'est pas flexible pour plus de types 2. Il ne permet pas la surcharge 3. __init__ est public, ce qui n'est pas forcément souhaitable si vous avez d'autres types de constructeurs.

14voto

David Z Points 49476

Dans votre cas, je vous suggère d'écrire une méthode de classe (ou une méthode statique ou une fonction distincte) qui prend en argument une instance de la classe de la bibliothèque et renvoie une instance de votre classe avec tous les attributs applicables copiés.

2 votes

En itérant à travers __dict__ peut-être ?

8voto

hobs Points 3020

En s'appuyant sur l'expérience de @Godsmith train de pensées et répondre au besoin de @Zitrax (je pense) de faire la copie des données pour tous les attributs dans le constructeur :

class ConfusionMatrix(pd.DataFrame):
    def __init__(self, df, *args, **kwargs):
        try:
            # Check if `df` looks like a `ConfusionMatrix`
            # Could check `isinstance(df, ConfusionMatrix)`
            # But might miss some "ConfusionMatrix-elligible" `DataFrame`s
            assert((df.columns == df.index).all())
            assert(df.values.dtype == int)
            self.construct_copy(df, *args, **kwargs)
            return
        except (AssertionError, AttributeError, ValueError):
            pass
        # df is just data, so continue with normal constructor here ...

    def construct_copy(self, other, *args, **kwargs):
        # construct a parent DataFrame instance
        parent_type = super(ConfusionMatrix, self)
        parent_type.__init__(other)
        for k, v in other.__dict__.iteritems():
            if hasattr(parent_type, k) and hasattr(self, k) and getattr(parent_type, k) == getattr(self, k):
                continue
            setattr(self, k, deepcopy(v))

Ce site ConfusionMatrix hérite d'une classe pandas.DataFrame et ajoute une tonne d'autres attributs et méthodes qui doivent être recalculés, à moins que l'utilisateur n'ait besoin d'une autre méthode. other les données de la matrice peuvent être copiées. C'est en cherchant une solution que j'ai trouvé cette question.

2 votes

Notez que cette approche diffère des constructeurs de copies de nombreux autres langages en ce sens qu'elle n'appelle pas les constructeurs de copies d'attributs.

0 votes

Notez également si v est un tableau numpy ou un dérivé, la comparaison avec == soulèvera une erreur : ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all() . Si vous avez des attributs qui peuvent ressembler à des tableaux numpy, une comparaison sans faille (qui fonctionne également avec des attributs ordinaires comme les ints, les strs, etc.) est la suivante np.array_equal

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X