Наследование: Атрибут от родительского класса - PullRequest
0 голосов
/ 05 октября 2018

В настоящее время я работаю над большим проектом на python и пытаюсь создать структуру, используя принципы ООП.У меня есть родительский класс, который содержит много атрибутов, а также несколько дочерних классов, которые должны использовать атрибуты из родительского класса.Однако у меня возникли некоторые проблемы при получении атрибутов из родительского класса из дочернего класса.В качестве упрощенного примера это то, что я хочу выполнить:

class A:
  def __init__(self, firstname):
    self.__firstname = firstname

  @property
  def firstname(self):
    return self.__firstname

class B(A):
  def __init__(self, lastname):
    self.__lastname = lastname
    A.__init__(self)

  @property
  def lastname(self):
    return self.__lastname

parent = A('somefirstname')
child = B(parent, 'somelastname')

print(child.firstname)
print(child.lastname)

Я получаю следующую ошибку при выполнении:

Traceback (most recent call last):
  File "classplay.py", line 23, in <module>
    child = B(parent, 'somelastname')
TypeError: __init__() takes 2 positional arguments but 3 were given

Ответы [ 3 ]

0 голосов
/ 05 октября 2018

Отвечая на собственный вопрос.Мой желаемый результат не возможен в Python.Однако, используя родительский класс в качестве аргумента в дочернем классе, мы можем достичь желаемого результата (но не без повторного ввода каждого необходимого аргумента для родительского класса)

class A:
  def __init__(self, firstname):
    self.__firstname = firstname

  @property
  def firstname(self):
    return self.__firstname

class B(A):
  def __init__(self, parent, lastname):
    self.__lastname = lastname
    A.__init__(self, parent.firstname)

  @property
  def lastname(self):
    return self.__lastname

parent = A('somefirstname')
child = B(parent, 'somelastname')

print(child.firstname)
print(child.lastname)
0 голосов
/ 05 октября 2018

Подходящая (Python 2.7) версия вашего кода будет выглядеть следующим образом:

class A(object):
  def __init__(self, firstname):
    self.firstname = firstname


class B(A):
  def __init__(self, lastname):
    super(B, self).__init__(firstname)
    self.lastname = lastname


parent = A('somefirstname')
child = B('somefirstname', 'somelastname')

print(child.firstname)
print(child.lastname)

Но я подозреваю, что вы действительно не понимаете ОО и особенно не наследование.В комментарии вы пишете:

Я считаю невероятно непродуктивным, что мне приходится создавать экземпляр родительского класса внутри дочернего класса.

Где вы это видели???Вызов __init__ суперкласса НЕ "создает экземпляр родительского класса", он просто применяет класс родителя __init__ к текущему дочернему экземпляру.Как еще можно правильно инициализировать дочерний экземпляр?

Неужели нет способа просто передать экземпляр родительского объекта дочернему классу и просто добавить новые атрибуты?

да, вы можете - просто вызовите метод класса и передайте экземпляр в качестве первого аргумента:

parent = A("firstname")
B.__init__(parent, "lastname")
print(parent.firstname, parent.lastname)

, но это НЕ будет делать то, что вы ожидаете - parent останется экземпляром A (хотя и с дополнительными атрибутами):

print(type(parent))

Кроме того, ваш класс B не совместим с A, поэтому он не является правильным подтипом A (согласно принципу подстановки Лискова).Технически это не является незаконным, и не обязательно является проблемой, но наследование связано не только с повторным использованием реализации, но и с подтипом (наследование обозначает отношение is a, поэтому «B наследует от A» означает, что «B»это (вид) А ").Так как наследование реализации в основном является ограниченным составом / делегированием падежа, вы можете дважды подумать о семантике своих моделей, прежде чем выбирать между наследованием и составлением / делегированием.

EDIT - это:

class A(object):
  def __init__(self, firstname):
    self.firstname = firstname


class B(A):
  def __init__(self, parent, lastname):
    self.lastname = lastname
    A.__init__(self, parent.firstname)

parent = A('somefirstname')
child = B(parent, 'somelastname')

это очень плохо спроектированная версия этого:

class A(object):
  def __init__(self, firstname):
    self.firstname = firstname


class B(A):
  def __init__(self, firstname, lastname):
    super(B, self).__init__(firstname)
    self.lastname = lastname


parent = A('somefirstname')
child = B(parent.firstname, 'somelastname')

И она плохо спроектирована, потому что она требует наличия A экземпляра - или чего-либо с атрибутом «firstname» - для создания B экземпляра, когда все, что нужно B - это имя.Вы всегда хотите ограничить связывание / зависимости между объектами до необходимого минимума.

FWIW также похоже, что вы путаете два разных восприятия «parent» и «child».

В OOP «parent» и «child» - это около классов , а не экземпляров и не подразумевают каких-либо прямых связей между экземплярами базового и производного классов (обратите внимание на использование «base» и «производного» вместо «parent» и «child» - этоменее двусмысленно).

Похоже, вы хотите смоделировать некоторые отношения между экземплярами («Люк, я твой отец», кто-нибудь?), где «ребенок» как «родитель» (а не «это").Наследование не является подходящим инструментом, вы хотите вместо него составление / делегирование:

class Father(object):
    def __init__(self, firstname):
        self.firstname = firstname


class Child(object):
    def __init__(self, father, lastname):
        self.father = father # composition
        self.lastname = lastname

   @property
   def firstname(self):
        # delegation
        return self.father.firstname

vador = Father("Vador")
luke = Child(vador, "Luke")
0 голосов
/ 05 октября 2018

Сначала я думаю, что вы неправильно понимаете наследство.Вы можете наследовать класс A от класса B, но не объект от другого объекта.

Во-вторых, вы допустили 2 следующие ошибки:

  • child = B(parent, 'somelastname'): при просмотре функции __init__ класса B запрашивается только один аргумент, например, child = B('somelastname') было бы в порядке.

  • A.__init__(self): при рассмотрении функции __init__ класса A запрашивается один аргумент, например, A.__init__('myfirstname') будет в порядке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...