0

I am currently working on a bigger project in python and am trying to establish structure using OOP principles. I have a parent class that contains a lot of attributes aswell as several child classes that has to utilize the attributes from the parent class. However I am having some trouble retrieving attributes from the parent class from the child class. As a simplified example this is what I want to accomplish:

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)

I get the following error when executing:

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
7
  • You don’t need the parent parameter in child = B(parent, 'somelastname') Commented Oct 5, 2018 at 12:51
  • Since you're inheriting A from B, B needs to initialize A fully. For example, B.__init__() could accept both firstname and lastname, then initialize the inherited A structure with firstname (i.e. super(B, self).__init__(firstname)) and store the lastname on top. The inheritance chain is linked automatically by Python so you don't need to provide a parent when initializing inherited objects. Commented Oct 5, 2018 at 12:52
  • @zwer That is what I have been able to gather from my searches on the subject. But say class A takes 10 arguments in its init method. Is there really not a way to simply pass the already instantiated A class to B instead of having to instantiate B with the same 10 arguments? Commented Oct 5, 2018 at 12:54
  • 1
    I think you should consider encapsulation instead of inheritance if you want to use properties of an already instantiated object. Commented Oct 5, 2018 at 13:02
  • 1
    Totally unrelated, but 1/ don't use double-leading-underscores names (ie self.__firstname unless you really understand what it does and what it's intended for - hint: the proper way to mark an attribute as "private"/"protected" is one single leading underscore and 2/ don't make an attribute protected for no good reason - you can always turn it into a computed attribute later if needed. Commented Oct 5, 2018 at 13:16

3 Answers 3

2

A proper (python 2.7) version of your code would be:

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)

But I suspect you don't really understand OO and specially not inheritance. In a comment you write:

I find it incredibly unproductive that I have to instantiate an instance of the parent class inside the child class.

Where have you seen this ??? The call to the superclass's __init__ is NOT "creating an instance of the parent class", it's just applying the parent's class __init__ on the current child instance. How else would the child instance be properly initialized ?

Is there really not a way to simply pass an instance of a parent to a child class and simply adding new attributes?

yes you can - just call the method on the class and pass the instance as first argument:

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

but this will NOT do what you expect - parent will remain an A instance (though with additional attributes):

print(type(parent))

Also, your B class is not compatible with A so it's not a proper subtype of A (according to liskov's substitution principle). This is not technically illegal, and not necessarily a problem, but inheritance is not only about implementation reuse, it's also about subtyping (inheritance denotes a is a relationship so "B inherits from A" means that "B is a (kind of) A"). Since implementation inheritance is mostly a restricted case composition/delegation, you may want to think twice about your models semantics before deciding between inheritance and composition/delegation.

EDIT - this:

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

is a very badly designed version of this:

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

And it's badly designed because it requires that you have an A instance -or anything having a "firstname" attribute - to create a B instance, when all B needs is the firstname. You always want to restrict coupling / dependencies between objects to the minimum required.

FWIW it also looks like you're confusing two different acceptations of "parent" and "child".

In OOP, "parent" and "child" are about classes, not instances, and do not imply any direct relationship between instances of the base and derived classes (note the use of "base" and "derived" instead of "parent" and "child" - this is less ambiguous).

Here it looks like you want to model some inter-instances relationships ("Luke, I'm your father" anyone ?) where "child" AS A "parent" (and not "is a"). Inheritance is not the proper tool here, you want composition/delegation instead:

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")
Sign up to request clarification or add additional context in comments.

Comments

0

First I think you are misunderstanding the inheritance. You can inherit a class A from a class B, but not an object from another object.

Secondly, you made the 2 following mistakes:

  • child = B(parent, 'somelastname'): looking at the __init__ function of class B, only one argument is requested, e.g. child = B('somelastname') would be OK.

  • A.__init__(self): looking at the __init__ function of class A, one argument is requested, e.g. A.__init__('myfirstname') would be OK.

1 Comment

Thanks for your answer. I see my errors. However I find it incredibly unproductive that I have to instantiate an instance of the parent class inside the child class. Is there really not a way to simply pass an instance of a parent to a child class and simply adding new attributes?
0

Answering own question. My desired outcome is not possible in python. However by using the parent class as an argument in the child class we can achieve the desired result (but not without retyping every needed argument for the parent class)

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)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.