Вопрос по inheritance, python – Наследование от экземпляра в Python

10

В Python я хотел бы создать экземпляр класса Child непосредственно из экземпляра класса Parent. Например:

A = Parent(x, y, z)
B = Child(A)

Это хак, который я думал, может сработать:

class Parent(object):

    def __init__(self, x, y, z):
        print "INITILIZING PARENT"
        self.x = x
        self.y = y
        self.z = z

class Child(Parent):

    def __new__(cls, *args, **kwds):
        print "NEW'ING CHILD"
        if len(args) == 1 and str(type(args[0])) == "<class '__main__.Parent'>":
            new_args = []
            new_args.extend([args[0].x, args[0].y, args[0].z])
            print "HIJACKING"
            return Child(*new_args)
        print "RETURNING FROM NEW IN CHILD"
        return object.__new__(cls, *args, **kwds)

Но когда я бегу

B = Child(A) 

Я получил:

NEW'ING CHILD  
HIJACKING  
NEW'ING CHILD  
RETURNING FROM NEW IN CHILD  
INITILIZING PARENT  
Traceback (most recent call last):  
  File "classes.py", line 52, in <module>  
    B = Child(A)  
TypeError: __init__() takes exactly 4 arguments (2 given) 

Кажется, хак работает так, как я и ожидал, но компилятор выдает TypeError в конце. Мне было интересно, могу ли я перегрузить TypeError, чтобы он игнорировал идиому B = Child (A), но я не был уверен, как это сделать. В любом случае, не могли бы вы дать мне ваши решения для наследования от экземпляров?

Спасибо!

Просто к сведению, "инициализация" пишется с 'a'; в этом. Попытался изменить это, но StackOverflow не позволит редактировать менее 6 букв. cfwschmidt

Ваш Ответ

7   ответов
5

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

_marker = object()

class Parent(object):

    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

class Child(Parent):

    _inherited = ['x', 'y', 'z']

    def __init__(self, parent):
        self._parent = parent
        self.a = "not got from dad"

    def __getattr__(self, name, default=_marker):
        if name in self._inherited:
            # Get it from papa:
            try:
                return getattr(self._parent, name)
            except AttributeError:
                if default is _marker:
                    raise
                return default

        if name not in self.__dict__:
            raise AttributeError(name)
        return self.__dict__[name]

Теперь, если мы сделаем это:

>>> A = Parent('gotten', 'from', 'dad')
>>> B = Child(A)
>>> print "a, b and c is", B.x, B.y, B.z
a, b and c is gotten from dad

>>> print "But x is", B.a
But x is not got from dad

>>> A.x = "updated!"
>>> print "And the child also gets", B.x
And the child also gets updated!

>>> print B.doesnotexist
Traceback (most recent call last):
  File "acq.py", line 44, in <module>
    print B.doesnotexist
  File "acq.py", line 32, in __getattr__
    raise AttributeError(name)
AttributeError: doesnotexist

Для более общей версии этого, посмотрите наhttp://pypi.python.org/pypi/Acquisition пакет. Это на самом деле в некоторых случаях кровавое решение проблемы.

2

что это очень старая тема, но недавно я столкнулся с той же проблемой, что и Александра, и это была самая актуальная тема, которую я мог найти. У меня был родительский класс со многими, многими атрибутами, и я хотел, по сути, «патчить»; его экземпляр, сохраняя все его методы и атрибуты, добавляя несколько и изменяя / перезаписывая другие. Простой подкласс не будет работать, потому что атрибуты будут заполнены пользователем во время выполнения, и я не смог просто унаследовать значения по умолчанию от родительского класса. После долгих попыток я нашел очень чистый (хотя и довольно хакерский) способ сделать это, используя__new__, Вот пример:

class Parent(object):
    def __init__(self):
        # whatever you want here
        self.x = 42
        self.y = 5
    def f(self):
        print "Parent class, x,y =", self.x, self.y

class Child(Parent):
    def __new__(cls, parentInst):
        parentInst.__class__ = Child
        return parentInst
    def __init__(self, parentInst):
        # You don't call the Parent's init method here
        self.y = 10
    def f(self):
        print "Child class, x,y =", self.x, self.y

c = Parent()
c.f()  # Parent class, x,y = 42 5
c.x = 13
c.f()  # Parent class, x,y = 13 5
c = Child(c)
c.f()  # Child class, x,y = 13 10

Единственная особая часть - это изменение__class__ атрибут родительского класса в конструкторе Child. Из-за этого ребенок__init__ Метод будет вызываться как обычно, и, насколько мне известно, класс Child должен функционировать точно так же, как и любой другой унаследованный класс.

Я также столкнулся с чем-то похожим:stackoverflow.com/questions/14177788/…
0

потому что я довольно конкретен, но это единственный связанный вопрос, который я нашел.

Если вы хотите наследовать атрибут, исходящий от родительского объекта посредством композиции, и без копирования какого-либо значения, вы можете сделать это:

class Agro:
    def __init__(self, parent, prop1=None, prop2=None):
        self.parent = parent
        self._prop1 = prop1
        self._prop2 = prop2

    @property
    def prop1(self):
        try:
            if self._prop1 is None:
                return self.parent.prop1
            else:
                return self._prop1
        except AttributeError as e:
            return None

    @property
    def prop2(self):
        try:
            if self._prop2 is None:
                return self.parent.prop2
            else:
                return self._prop2
        except AttributeError as e:
            return None

Тогда вы указываете:

ag = Agro(None, prop1="nada")  # prop1: nada, prop2: None
ag2 = Agro(ag, prop1="nada2")  # prop1: nada2, prop2: None
ag3 = Agro(ag2, prop2="youhou")  # prop1: nada2, prop2: youhou
ag4 = Agro(ag3)  # prop1: nada2, prop2: youhou

Что интересно, когда вы смотрите на то, что на самом деле сохраняется в случаях с__dict__ атрибуты сохраняются только один раз. Поэтому вы можете динамически изменять значение атрибута:

ag3._prop2 = "yaya"
print(ag4.prop2)  # yaya
7

__new__ в классеChild возвращает экземплярChild, Child.__init__ будет вызван (с теми же аргументами__new__ был дан) в этом случае - и, видимо, он просто наследуетParent.__init__который не очень хорошо вызывается с одним аргументом (другимParent, A).

Если нет другого способаChild можно сделать, вы можете определитьChild.__init__ который принимает либо один аргумент (который игнорирует), либо три (в этом случае он вызываетParent.__init__). Но отказаться проще__new__ и есть вся логика вChild.__init__звонюParent.__init__ надлежащим образом!

Чтобы сделать это конкретным с примером кода:

class Parent(object):

    def __init__(self, x, y, z):
        print "INITIALIZING PARENT"
        self.x = x
        self.y = y
        self.z = z

    def __str__(self):
        return "%s(%r, %r, %r)" % (self.__class__.__name__,
            self.x, self.y, self.z)


class Child(Parent):

    _sentinel = object()

    def __init__(self, x, y=_sentinel, z=_sentinel):
        print "INITIALIZING CHILD"
        if y is self._sentinel and z is self._sentinel:
            print "HIJACKING"
            z = x.z; y = x.y; x = x.x
        Parent.__init__(self, x, y, z)
        print "CHILD IS DONE!"

p0 = Parent(1, 2, 3)
print p0
c1 = Child(p0)
print c1
c2 = Child(4, 5, 6)
print c2
2

class Child(object):
    def __init__(self):
        self.obj = Parent()

    def __getattr__(self, attr):
        return getattr(self.obj, attr)

Таким образом, вы можете использовать все методы Parent и ваши собственные, не сталкиваясь с проблемами наследования.

0

ребята, это было быстро! Сначала я прочитал комментарий Алекса и переписал ребенка.__init__ как

def __init__(self, *args, **kwds):
    if len(args) == 1 and str(type(args[0])) == "<class '__main__.Parent'>":
        new_args = [args[0].x, args[0].y, args[0].z]
        super(Child, self).__init__(*new_args, **kwds)
    else:
        super(Child, self).__init__(*args, **kwds)

что очень похоже на то, что предложил abhinavg (как я только что узнал). И это работает. Только его и ars & apos; линия

if len(args) == 1 and isinstance(args[0], Parent):

чище чем у меня.

Еще раз спасибо!!

4

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

class Child(Parent):
    def __init__(self, *args, **kwargs):
        if len(args) == 1 and isinstance(args[0], Parent):
            Parent.__init__(self, args[0].x, args[0].y, args[0].z)

        else:
            # do something else
+1: просто создайте Child, копируя атрибуты родителя.

Похожие вопросы