web-dev-qa-db-fra.com

Copier le constructeur en python?

Existe-t-il un constructeur de copie dans python? Sinon, que ferais-je pour obtenir quelque chose de similaire?

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

77
Zitrax

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 pickle .

61
Tom Dunham

Dans 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 effectuer les opérations suivantes:

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
22
qwerty9967

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)
14
Godsmith

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

11
David Z

En s'appuyant sur @ Godsmith train de pensée et en répondant 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))

Cette classe ConfusionMatrix 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 puissent être copiées. C'est à la recherche d'une solution que j'ai trouvé cette question.

4
hobs

J'ai une situation similaire qui diffère en ce que la nouvelle classe n'a besoin que de copier les 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

Le résultat:

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


foo2
 two: <type 'int'>, 2
 one: <type 'str'>, one
2
upandacross