2009-08-06 9 views
60

Czy istnieje konstruktor kopii w python? Jeśli nie, to co zrobiłbym, aby osiągnąć coś podobnego?Skopiuj konstruktor w pythonie?

Sytuacja polega na tym, że korzystam z biblioteki i rozszerzyłem jedną z klas o dodatkową funkcjonalność i chcę móc przekonwertować obiekty, które otrzymuję z biblioteki na instancje własnej klasy.

+0

Możesz być zainteresowany w tej kwestii [Disclaimer: był ten, który go o to poprosił]: http://stackoverflow.com/questions/990758/reclassing-an-instance-in-python – balpha

+0

Bądź ostrożny. Ostrzeżenia zamieszczone przez niektórych odbierających nie mogą być niedoceniane. – balpha

+0

Do tego nie wygląda na bardzo czytelny, prawdopodobnie zmienię mój kod z używania dziedziczenia, aby zamiast niego zamykać tylko drugi obiekt. – Zitrax

Odpowiedz

48

myślę chcesz copy module

import copy 

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

można kontrolować kopiowanie w taki sam sposób, jak można kontrolować pickle.

+3

'Deepcopy' działa dobrze, robiąc to na zewnątrz do definicji klasy. Ale @Zitrax chce to zrobić w swojej definicji klasy, aby nowa instancja dziedziczyła atrybuty (dane) z obiektu innego (rodzica) typu (klasy). – hobs

10

Dla twojej sytuacji, proponuję napisać metodę klasy (lub może to być metoda statyczna lub osobna funkcja), która przyjmuje jako argument instancję klasy biblioteki i zwraca instancję klasy z wszystkimi odpowiednimi atrybutami skopiowane.

+0

przez iterowanie przez '__dict__' może? – hobs

14

W pythonie konstruktor kopii można zdefiniować za pomocą domyślnych argumentów. Powiedzmy, że chcesz, aby normalny konstruktor uruchamiał funkcję non_copy_constructor(self), a konstruktor kopii powinien uruchomić copy_constructor(self, orig). Następnie można wykonać następujące czynności:

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 
8

Prostym przykładem mojego zwykłego realizacji konstruktora kopię:

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) 
+0

Nice. To działałoby jak 'Foo.from_foo (foo)'. Dalszym udoskonaleniem byłoby sprawienie, by działało to na 'Foo (foo)', czego prawdopodobnie chce OP @Zitrax. – hobs

3

Opierając się na @ Godsmith na train of thought i adresowanie @ potrzebie Zitrax jest (myślę), aby wykonać kopiowanie danych dla wszystkich atrybutów w konstruktorze:

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

Ten ConfusionMatrix klasa dziedziczy pandas.DataFrame i dodaje mnóstwo innych Attribut es i metody, które muszą zostać przeliczone, chyba że dane macierzy other można skopiować. Szukając rozwiązania, znalazłem to pytanie.

1

Podobna sytuacja różni się tym, że nowa klasa musi tylko kopiować atrybuty. W ten sposób za pomocą @ idei Dunham i dodając pewną specyficzność do użytkownika @ meisterluk sugestią @ "copy_constructor" metody meisterluk mogłoby być:

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 

dane wyjściowe:

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


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