Copier le constructeur en python?

Existe-t-il un constructeur de copie en python ? Si ce n'est pas le cas, que ferais-je pour réaliser quelque chose de similaire ?

La situation est que j'utilise une bibliothèque et j'ai étendu l'une des classes avec des fonctionnalités supplémentaires et je veux pouvoir convertir les objets que je reçois de la bibliothèque en instances de ma propre classe.

66
demandé sur martineau 2009-08-07 00:16:48

6 réponses

Je pense que vous voulez l' 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 pickle.

55
répondu Tom Dunham 2009-08-06 20:21:34

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 que le constructeur de copie exécute copy_constructor(self, orig). Ensuite, vous pouvez faire ce qui 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
17
répondu qwerty9967 2017-05-15 06:11:04

Pour votre situation, je suggère d'écrire une méthode de classe (ou il pourrait s'agir d'une méthode statique ou d'une fonction distincte) qui prend comme argument une instance de la classe de la bibliothèque et renvoie une instance de votre classe avec tous les attributs applicables copiés.

10
répondu David Z 2009-08-06 20:31:14

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)
8
répondu Godsmith 2014-12-12 10:37:17

Construire sur le train de pensée de @Godsmith {[6] } et répondre au besoin de @Zitrax (je pense) de faire la copie de données pour tous les attributs du 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 ConfusionMatrix la classe hérite d'un pandas.DataFrame et ajoute une tonne d'autres attributs et méthodes qui doivent être recalculés à moins que les données de la matrice other ne puissent être copiées. La recherche d'une solution est la façon dont j'ai trouvé cette question.

3
répondu hobs 2017-05-23 11:54:46

J'ai une situation similaire différant en ce que la nouvelle classe n'a besoin que de copier des attributs. Ainsi, en utilisant l'idée de @Dunham et en ajoutant une certaine spécificité à la suggestion de @meisterluk, la méthode "copy_constructor" de @meisterluk pourrait être:

from copy import deepcopy
class Foo(object):
    def __init__(self, myOne=1, other=None):
    self.two = 2
    if other <> None:
        assert isinstance(other, Foo), "can only copy instances of Foo"
        self.__dict__ = deepcopy(other.__dict__)
    self.one = myOne

def __repr__(self):
    out = ''
    for k,v in self.__dict__.items():
        out += '{:>4s}: {}, {}\n'.format(k,v.__class__,v)
    return out

def bar(self):
    pass

foo1 = Foo()
foo2 = Foo('one', foo1)

print '\nfoo1\n',foo1
print '\nfoo2\n',foo2

La sortie:

foo1
 two: <type 'int'>, 2
 one: <type 'int'>, 1


foo2
 two: <type 'int'>, 2
 one: <type 'str'>, one
1
répondu upandacross 2017-08-27 16:43:42