I came across this example of method inheritance:
The child Manager class inherits from the parent Employee class, and modifies its give_raise() method, while also calling Employee.give_raise() in the new implementation.
# Parent class
class Employee:
def __init__(self, name, salary=30000):
self.name = name
self.salary = salary
def give_raise(self, amount):
self.salary += amount
# Child class
class Manager(Employee):
def display(self):
print("Manager ", self.name)
def __init__(self, name, salary=50000, project=None):
Employee.__init__(self, name, salary)
self.project = project
# Modify the give_raise method
def give_raise(self, amount, bonus=1.05):
new_amount = amount * bonus
# Call the parent method
Employee.give_raise(self, new_amount)
mngr = Manager("John Smith", 80000)
mngr.give_raise(5000)
print(mngr.salary)
My question is: isn't this a bit convoluted?
I get class inheritance. But calling a parent method within a child method of the same name?
Wouldn't it be better to write give_raise() in the Manager class as:
def give_raise(self, amount, bonus):
new_amount = amount * bonus
self.salary += amount
To me, it only makes sense to call Parent.method() if it's a completely different method that requires a lot of lines of code
I agree with you initial question in that the implementations are a bit convoluted and don't help to clearly convey the intended usage. The main goal of method inheritance is to encourage reusable, encapsulated behavior within the class hierarchy. That way the derived class could add some functionality or override the underlying functionality (or leave it be and add other functionality).
Parent.give_raise could be defined in a way that doesn't require it to be overriden in order to customize what raise different employee types get.
For example,
class Employee:
default_salary = 30000
def __init__(self, name, salary=None, bonus=1, **kwargs):
super().__init__(**kwargs)
if salary is None:
salary = self.default_salary
self.name = name
self.salary = salary
self.bonus_multiplier = bonus
def give_raise(self, amount):
self.salary += amount * self.bonus_multiplier
class Manager(Employee):
default_salary = 50000
def display(self):
print("Manager ", self.name)
def __init__(self, *, project=None, **kwargs):
if 'bonus' not in kwargs:
kwargs['bonus'] = 1.05
super.__init__(**kwargs)
self.project = project
mngr = Manager(name="John Smith", salary=80000)
mngr.give_raise(5000)
print(mngr.salary)
Related
I need to change a inherited class to another inherited class where only one of the attributes has changed
i need to "Promote" a Cashier to a Manager, the only thing that is suppose to change is the salary
both Cashier and Manager are inherited classes of Employee (where I'm not sure if I'm using the "hasattr" function the right way)
class Employee:
def __init__(self,name):
self.name=name
if(hasattr(self,'shifts')==False):
self.shifts=[]
class Manager(Employee):
def __init__(self,name,salary):
Employee.__init__(self,name)
self.salary=salary
class Cashier(Employee):
def __init__(self,name,salarey_per_hours):
Employee.__init__(self,name)
self.salery_per_hours=salarey_per_hours
def promote(self,salary):
return Manager(self.name,salary)
P.s It's my first time uploading a question
What you could do is create the addition method of your class and add self to the manager class you are returning like so:
class Employee(object):
def __init__(self, name):
self.name=name
if not hasattr(self, 'shifts'):
self.shifts = []
def __add__(self, other):
if isinstance(other, Employee):
for key, value in other.__dict__.items():
if key == 'salary':
continue
self.__setattr__(key, value)
return self
class Manager(Employee):
def __init__(self, name, salary):
super().__init__(name)
self.salary = salary
class Cashier(Employee):
def __init__(self,name,salary):
super().__init__(name)
self.salary = salary
def promote(self, salary):
manager = Manager(self.name, salary)
manager += self
return manager
cashier = Cashier('hank', 22)
cashier.shifts = [1, 2, 3, 4]
print(cashier.shifts)
promoted_cashier = cashier.promote(30)
print(promoted_cashier.shifts)
Here you make sure that everything except the "salary" is transferred to the promoted class. And since both the Manager and the Cashier are an Employee this should work nicely. I changed your code a bit to what I'm used to since there was some unusual coding with you Calling Employee in the init which I assumed you did not explicitly needed. Sorry if that was not the case.
You can change the object's class by obj.__class__ to the another class by
doing obj.__class__ = SomeClass
Beware that is can lead to strange behaviours if it is handled incorrectly.
by modifying your code
class Employee:
def __init__(self,name):
self.name=name
if(hasattr(self,'shifts')==False):
self.shifts=[]
class Manager(Employee):
def __init__(self,name,salary):
Employee.__init__(self,name)
self.salary=salary
class Cashier(Employee):
def __init__(self,name,salarey_per_hours):
Employee.__init__(self,name)
self.salery_per_hours=salarey_per_hours
def promote(self,salary):
self.__class__ = Manager
# return Manager(self.name,salary)
You can also take a look at this post changing the class of a python object (casting)
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.
Suppose I have a class:
class Person(object):
def __init__(self, name, hobbies):
self.name = name
self.hobbies = hobbies
... (and so on)
Now I want to initialise a child class, Employee, which extends person. I would like to initialise that class with an instance of the Person class. So I would like to do:
class Employee(Person):
def __init__(self, person, salary):
# Initialise the superclass from the given instance somehow
# I know I could do:
super(Employee, self).__init__(person.name, person.hobbies)
# But could I somehow do something like:
super(Employee, self).__init__(person)
# (In this case the difference is small, but it could
# be important in other cases)
# Add fields specific to an "Employee"
self.salary = salary
So that I can then call:
p1 = Person('Bob', ['Bowling', 'Skiing'])
employed_p1 = Employee(p1, 1000)
Is there any way I can do this, or do I explicitly have to call the parent class's constructor again?
Thanks a lot!
I thnk you want something like this:
class Person(object):
def __init__(self, name, hobbies):
self.name = name
self.hobbies = hobbies
def display(self):
print(self.name+' '+self.hobbies[0])
class Employee(Person):
def __init__(self, a, b =None,salary=None):
if b is None:
self.person = a
else:
self.person = Person(a,b)
self.name = self.person.name
self.hobbies = self.person.hobbies
self.salary = salary
bob = Employee('bob',['Bowling', 'Skiing'])
bob.display()
sue1 = Person('sue',['photography','music'])
sue2 = Employee(sue1,salary=123)
sue2.display()
I've added in the 'display' function just to make it easier to follow. Hope this helps.
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
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?