Change a inherited class to another inherited class keeping the attributes - python

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)

Related

What's the point of method inheritance and calling Parent.method()?

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)

Class Inheritance with parameters

I have a parent class and several subclasses. Every subclass accepts different parameters, but all subclasses have some common parameters. I don't want to write the "common parameters" for every subclass. How can I do this?
class Parent:
def __init__(self, name):
self.name = name
class Subclass(Parent):
def __init__(self, age):
self.age = age
def do_something(self):
print(self.name)
instance = Subclass(name="Test", age=42)
instance.do_something() # 42
You can try this:
class Subclass(Parent):
def __init__(self, **kwargs):
super().__init__(kwargs['name'])
self.age = kwargs['age']
def do_something(self):
print(self.name)
And then use this just like you did in the question:
instance = Subclass(name="Test", age=42)
I use it in the following manner
You can add as many child classes as you want
class ParentClass(object):
def __init__(self,baseArgs):
self.var1=baseArgs['var1']
self.var2=baseArgs['var2']
self.var3=baseArgs['var3']
class ChildClass(ParentClass):
def __init__(self,childArgs,baseArgs):
super(ChildClass, self).__init__(baseArgs)
self.cvar1=childArgs['cvar1']
self.cvar2=childArgs['cvar2']
a=ChildClass({'cvar1':40,'cvar2':50},{'var1':10,'var2':20,'var3':30})
print(a.var1)
# 10

Accessing a method of a parent class in the sublass

i am new to OOP.
i have a parent class with a method that i wanna access in my subclass. But i cannot figure out the right syntax for that. I cannot find a clear cut example anywhere
Members in the base class are simply available to the subclass as well (unless they are overwritten):
class Base:
def example (self):
print('This is in the base class')
class Subclass (Base):
def test (self):
self.example()
An object of type Subclass can now access example directly or indirectly:
>>> x = Subclass()
>>> x.test()
This is in the base class
>>> x.example()
This is in the base class
class Parent(object):
def __init__(self, name):
self.name = name
def output(self):
print self.name
class Child(Parent):
def __init__(self, name, age):
Parent.__init__(self, name)
self.age = age
def output(self):
super(Child, self).output()
print self.age
if __name__ == '__main__':
a = Parent("wy")
b = Child("zhang", 10)
a.output()
b.output()
You can try this code.

Not able to understand how iterator works on class

from __future__ import print_function
class Employee:
def __init__(self, name, salary=0):
self.name = name
self.salary = salary
def giveRaise(self, percent):
self.salary = self.salary + (self.salary * percent)
def work(self):
print(self.name, "does stuff")
def __repr__(self):
return "<Employee: name=%s, salary=%s>" % (self.name, self.salary)
class Chef(Employee):
def __init__(self, name) :
Employee.__init__(self, name, 50000)
def work(self) :
print(self.name, "makes food")
class Server(Employee):
def __init__(self, name):
Employee.__init__(self, name, 40000)
def work(self):
print(self.name, "interfaces with customer")
class PizzaRobot(Chef):
def __init__(self, name):
Chef.__init__(self, name)
def work(self):
print(self.name, "makes pizza")
if __name__ == "__main__":
for klass in Employee, Chef, Server, PizzaRobot:
obj = klass(klass.__name__)
obj.work()
In above code I am not able to understand how last 3 lines behaves whether klass would be a class or it would be an instance of class.
if its an class then what below line means and why it is required?
obj = klass(klass.__name__)
As mentioned there's no iterator here, but a tuple of classes. The loop below will loop over each class and instantiate a new object for each of the classes.
for klass in Employee, Chef, Server, PizzaRobot:
obj = klass(klass.__name__)
obj.work()
If you look at your class definitions and specifically the __init__-function for each class, you'll see that there's a positional argument name:
class Chef(Employee):
def __init__(self, name): # <-- look here.
Which means that each of your classes must be instantiated with a parameter name:
>>> john = Chef('John')
>>> print(john)
<Employee: name=John, salary=50000>
Each class has a __name__ attribute, which is the defined name for that class. For example Chef.__name__ is Chef:
>>> Chef.__name__
'Chef'
This is different from the name parameter you've defined for your classes, as this is an internal attribute and should not be changed.
Thus the line obj = klass(klass.__name__) will create an object for each the classes you're looping over and give the class __name__ attribute as the positional argument name for each object.
You will end up with four objects of types Employee, Chef, Server and PizzaRobot. Instead of human names like John as per the example I've given, you're just naming them after the class.
>>> for klass in Employee, Chef, Server, PizzaRobot:
... print(klass.__name__)
...
Employee
Chef
Server
PizzaRobot
>>>

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