Скопировать конструктор в python? - PullRequest
75 голосов
/ 07 августа 2009

Есть ли в Python конструктор копирования? Если нет, то что бы я сделал, чтобы добиться чего-то подобного?

Ситуация такова, что я использую библиотеку, и я расширил один из классов там с дополнительными функциями, и я хочу иметь возможность преобразовывать объекты, которые я получаю из библиотеки, в экземпляры моего собственного класса.

Ответы [ 6 ]

60 голосов
/ 07 августа 2009

Я думаю, вы хотите модуль копирования

import copy

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

вы можете управлять копированием так же, как вы управляете pickle .

21 голосов
/ 17 апреля 2013

В python конструктор копирования может быть определен с использованием аргументов по умолчанию. Допустим, вы хотите, чтобы обычный конструктор запускал функцию non_copy_constructor(self), а конструктор копирования должен запускать copy_constructor(self, orig). Тогда вы можете сделать следующее:

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
14 голосов
/ 12 декабря 2014

Простой пример моей обычной реализации конструктора копирования:

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)
11 голосов
/ 07 августа 2009

В вашей ситуации я бы предложил написать метод класса (или это может быть статический метод или отдельную функцию), который принимает в качестве аргумента экземпляр класса библиотеки и возвращает экземпляр вашего класса со всеми скопированными применимыми атрибутами. более.

4 голосов
/ 16 февраля 2016

Опираясь на ход мыслей @ Godsmith и обращаясь к необходимости @ Zitrax (я думаю) сделать копию данных для всех атрибутов в конструкторе:

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

Этот класс ConfusionMatrix наследует pandas.DataFrame и добавляет тонну других атрибутов и методов, которые необходимо пересчитать, если только данные матрицы other не могут быть скопированы. В поисках решения я нашел этот вопрос.

2 голосов
/ 27 августа 2017

У меня похожая ситуация, отличающаяся тем, что новому классу нужно только копировать атрибуты. Таким образом, используя идею @ Dunham и добавляя некоторую специфичность предложению @ meisterluk, метод @ meisterluk "copy_constructor" может быть следующим:

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

Выход:

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


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