Resolving Diamond Inheritance within Python Classes - python

Consider the following python code:
class Parent(object):
def __init__(self, name, serial_number):
self.name = name
self.serial_number = serial_number
class ChildA(Parent):
def __init__(self, name, serial_number):
self.name = name
self.serial_number = serial_number
super(ChildA, self).__init__(name = self.name, serial_number = self.serial_number)
def speak(self):
print("I am from Child A")
class ChildB(Parent):
def __init__(self, name, serial_number):
self.name = name
self.serial_number = serial_number
super(ChildB, self).__init__(name = self.name, serial_number = self.serial_number)
def speak(self):
print("I am from Child B")
class GrandChild(ChildA, ChildB):
def __init__(self, a_name, b_name, a_serial_number, b_serial_number):
self.a_name = a_name
self.b_name = b_name
self.a_serial_number = a_serial_number
self.b_serial_number = b_serial_number
super(GrandChild, self).__init_( something )
When running the super function in GrandChild, what is the proper way to format the __init__ arguments so that ChildA and ChildB both get the correct arguments?
Also how do you access the two different versions of the speak method (ChildA's version and ChildB's version) from within the GrandChild class?

so, when you call super from the grandchild, ChildA's __init__ method will be called because super follows the __mro__ property (parents left to right then grandparents left-to-right, then great grandparents, ...)
Since ChildA's init also calls super, then all the super calls will be chained, calling child b's __init__ and eventually the parent init.
For that to work, your interface generally needs to be consistent. That is positional arguments need to mean the same things, and be in the order.
In situations where that's not the case, keyword arguments may work better.
class Parent:
def __init__(self, name, serial, **kwargs):
self.name = name
self.serial = serial
class ChildA(Parent):
def __init__(self, a_name, a_serial, **kwargs):
self.a_name = a_name
self.a_serial = a_serial
super().__init__(**kwargs)
class ChildB(Parent):
def __init__(self, b_name, b_serial, **kwargs):
self.b_name = b_name
self.b_serial = b_serial
super().__init__(**kwargs)
class GrandChild(ChildA, ChildB):
def __init__(self):
super().__init__(name = "blah", a_name = "a blah", b_name = "b blah", a_serial = 99, b_serial = 99, serial = 30)
Also note that in your code name and serial are reused as instance properties between all the classes and that's probably not what you want.

In python, you can explicitly call a particular method on (one of) your parent class(es):
ChildA.__init__(self, a_name, a_serial)
ChildB.__init__(self, b_name, b_serial)
Note that you need to put the self in explicitly when calling this way.
You can also – as you did – use the super() way, which will call the "first" parent. The exact order is dynamic, but by default it will do left-to-right, depth-first, pre-order scans of your inheritance hierarchy. Hence, your super() call will only call __init__ on ChildA.

Related

How to call a child class with no parameter in contructor in python?

I have a Multiple Parent class and the superparent class is the
class Item:
def __init__(self, name):
self.name = name
def print_name(self):
print("The item name is " + self.name)
and the base class is:
class Gadget(Item):
def __init__(self, name,
version):
self.name = name
self.version = version
def print_attribute(self):
pass
#do for attribute
The child of the base class is:
class Mobile_Phone(Gadget):
def __init__():
pass
so when i instatiate the child class
item = Mobile_Phone("Iphone", "iOS")
item.print_name()
it gives me an error the contuctor has 0 positional argument but 3 were given
You need to understand the concept of OOPs and if you send arguments during object construction then your constructor should have parameters to hold the arguments.
item.py
class Item:
def __init__(self, name):
self.name = name
def print_name(self):
print("The item name is " + self.name)
Gadget.py
from item import Item
class Gadget(Item):
def __init__(self, name, version):
Item.__init__(self, name)
self.version = version
def print_attribute(self):
print(self.name)
print(self.version)
Mobile_Phone.py
from Gadget import Gadget
class Mobile_Phone(Gadget):
def __init__(self, name, version):
Gadget.__init__(self, name, version)
item = Mobile_Phone("Iphone", "iOS")
item.print_name()
Output:
The item name is Iphone
If your child class initializer doesn't do anything with the arguments you're passing, then they're lost. In the model you've outlined, you could simply omit the initializer in the child class to get what you want.
class MobilePhone(Gadget): # PEP8 calls for CamelCase here
pass
In this case, the initializer inherited from Gadget is setting self.name which the inherited Item.print_name references.

How to call attribute from parent class?

I am currently learning how to program in Python I am stuck calling an attribute from a Parent class. In the example below, how can I call the attribute "name" on "daisy" to print the name. I always get an error "'Mammal' object has no attribute 'name'.
class Vertebrate:
spinal_cord = True
def __init__(self, name):
self.name = name
class Mammal(Vertebrate):
def __init__(self, name, animal_type):
self.animal_type = animal_type
self.temperature_regulation = True
daisy = Mammal('Daisy', 'dog')
print(daisy.name)
here I want to print the name which has been defined in the Vertebrate class, but I always get an error
"'Mammal' object has no attribute 'name'"
You need to call super in the __init__ of Mammal, like this:
class Mammal(Vertebrate):
def __init__(self, name, animal_type):
super().__init__(name)
self.animal_type = animal_type
self.temperature_regulation = True
When __init__ of Mammal is called, it doesn't automatically call the __init__ of it's parent class, that's what super does here.
When you assign an init function to the child class, it overrides the default init function of parent class being called. In such a case you need to explicitly call the parent class using the super function. You also need to assign the class Vertebrate to be a child of the class Object to be able to access all of the object's modules within it.
class Vertebrate(object):
def __init__(self, name):
self.name = name
spinal_cord = True
class Mammal(Vertebrate):
def __init__(self, name, animal_type):
super(Mammal, self).__init__(name)
self.animal_type = animal_type
self.temperature_regulation = True
animal = Mammal("Daisy", "dog")
print animal.name

Python RPG Inheritance Issues?

I am running into a TypeError for my super().init method stating it only takes one positional argument and three are given. I assumed that the Enchanted class inherited the other parameters from the parent classes Weapons and Item, but I seemed to have missed something?
Using python 3.5 and link to the GitHub repository if needed is here: PythonRPG.
#base item class
class Item(object):
def __init__(self, name, description):
self.name = name
self.description = description
def __init__(self):
return "{}\n=====\n{}\nDamage: {}".format(self.name, self.description)
#start weapons
class Weapons(Item):
def __init__(self, name, description, attack):
self.attack = attack
super().__init__(name, description)
def __str__(self):
return "{}\n=====\n{}\nDamage: {}".format(self.name, self.description, self.attack)
class Enchanted(Weapons):
def __init__(self):
#error appears here
super().__init__(name="Enchanted Longsword", description="A prestine longsword you found with small runes inscribed along the Cross Guard. You feel a small magical force emanating from the weapon as you hold it.", attack = 12)
You have two __init__ methods in your Item class. The second overwrites the first, and since it takes only one positional parameter (self), the error is thrown. Simple fix: get rid of the second __init__.
I'm not certain, but perhaps you meant your second __init__ to be __str__?
class Item(object):
def __init__(self, name, description):
self.name = name
self.description = description
def __str__(self):
return "{}\n=====\n{}\nDamage: {}".format(self.name, self.description)
A nice answer is already provided by jme. I am guessing that you are trying something like method overloading or creating different constructors for Item class.
In a python class you can not have two methods of same name. But you can achieve this property by providing default values to the method parameters. Something like this:
class Item(object):
def __init__(self, name=None, description=None):
self.name = name
self.description = description

Get attribute from a super class in python

I have a base class, a bunch of subclasses, and for each of these subclasses, I have another set of sub-subclasses. For example:
class BaseClass(object):
def __init__(self):
with open(config.txt) as f
self.config_array = f.readlines()
class FirstOrderSubClass(BaseClass):
def __init__(self, name):
self.name = name
class SecondOrderSubClass(FirstOrderSubClass):
def __init__(self, name, version):
self.name = name
self.version = version
super(SecondOrderSubClass, self).__init__(self.name)
# needed to access self.config_array
print self.config_array
I need to get the __init__() method of the SecondOrderSubClass to make the following assignment: self.lines = self.config_array.
EDIT: added line print self.config_array. If I run the code I get:
TypeError: __getattr__() takes exactly 1 argument (2 given)
You cannot access self.config_array until BaseClass.__init__() has run to set the attribute.
Either fix FirstOrderSubClass to also invoke the base class __init__ or call it directly.
Fixing the FirstOrderSubClass is probably the best way to do so:
class FirstOrderSubClass(BaseClass):
def __init__(self, name):
super(FirstOrderSubClass, self).__init__()
self.name = name
However, your __init__ method signatures do not match so you cannot rely on cooperative behaviour here; as soon as you add a mix-in class in the hierarchy, things can and probably will break. See *Python's super() is considered super! by Raymond Hettinger, or it's followup PyCon presentation to explain why you want your signatures to match.
Calling the BaseClass.__init__ unbound method directly (passing in self explicitly) would also work:
class SecondOrderSubClass(FirstOrderSubClass):
def __init__(self, name, version):
super(SecondOrderSubClass, self).__init__(name)
self.version = version
BaseClass.__init__(self)
Note that there is no point in assigning to self.name there if you are going to ask FirstOrderSubClass.__init__ to do the exact same thing.
The proper way to use super() is for all your methods to at least accept all the same arguments. Since object.__init__() never does, this means you need a sentinel class that does not use super(); BaseClass will do nicely here. You can use *args and **kw to capture any additional arguments and just ignore those to make cooperative subclassing work:
class BaseClass(object):
def __init__(self, *args, **kw):
with open(config.txt) as f
self.config_array = f.readlines()
class FirstOrderSubClass(BaseClass):
def __init__(self, name, *args, **kw):
super(FirstOrderSubClass, self).__init__(*args, **kw)
self.name = name
class SecondOrderSubClass(FirstOrderSubClass):
def __init__(self, name, version, *args, **kw):
super(SecondOrderSubClass, self).__init__(name, *args, **kw)
self.version = version
You have to call the FirstOrderSubClass super method:
class BaseClass(object):
def __init__(self):
with open("config.example.txt",'w') as f:
f.write("Hello world")
with open("config.example.txt") as f:
self.config_array = f.readlines()
class FirstOrderSubClass(BaseClass):
def __init__(self, name):
super(FirstOrderSubClass,self).__init__()
self.name = name
class SecondOrderSubClass(FirstOrderSubClass):
def __init__(self, name, version):
self.name = name
self.version = version
super(SecondOrderSubClass, self).__init__(self.name)
# needed to access self.config_array
grandchild = SecondOrderSubClass("peter",2.0)
print grandchild.config_array
##>>>
##['Hello world']

Subclass not inheriting parent class

I'm having trouble with my code. I'm trying to create a subclass which inherits the parent class's attributes and methods but it doesn't work. Here's what I have so far:
class Employee(object):
def __init__(self, emp, name, seat):
self.emp = emp
self.name = name
self.seat = seat
Something is wrong with the block of code below - the subclass.
Do I have to create the __init__ again? And how do I create a new attribute for the subclass. From reading questions, it sounds like __init__ in the subclass will override the parent class - is that true if I call it to define another attribute?
class Manager(Employee):
def __init__(self, reports):
self.reports = reports
reports = []
reports.append(self.name) #getting an error that name isn't an attribute. Why?
def totalreports(self):
return reports
I want the names from the Employee class to be in the reports list.
For example, if I have:
emp_1 = Employee('345', 'Big Bird', '22 A')
emp_2 = Employee('234', 'Bert Ernie', '21 B')
mgr_3 = Manager('212', 'Count Dracula', '10 C')
print mgr_3.totalreports()
I want reports = ['Big Bird', 'Bert Ernie'] but it doesn't work
You never called the parent class's __init__ function, which is where those attributes are defined:
class Manager(Employee):
def __init__(self, reports):
super(Manager, self).__init__()
self.reports = reports
To do this, you'd have to modify the Employee class's __init__ function and give the parameters default values:
class Employee(object):
def __init__(self, emp=None, name=None, seat=None):
self.emp = emp
self.name = name
self.seat = seat
Also, this code will not work at all:
def totalreports(self):
return reports
reports's scope is only within the __init__ function, so it will be undefined. You'd have to use self.reports instead of reports.
As for your final question, your structure won't really allow you to do this nicely. I would create a third class to handle employees and managers:
class Business(object):
def __init__(self, name):
self.name = name
self.employees = []
self.managers = []
def employee_names(self);
return [employee.name for employee in self.employees]
You'd have to add employees to the business by appending them to the appropriate list objects.
You need to run the superclass's init() in the appropriate place, plus capture the (unknown to the subclass) arguments and pass them up:
class Manager(Employee):
def __init__(self, reports, *args, **kwargs):
self.reports = reports
reports = []
super(Manager, self).__init__(*args, **kwargs)
reports.append(self.name) #getting an error that name isn't an attribute. Why?

Categories