Not able to understand how iterator works on class - python

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

Related

Can someone clarify inheriting private attributes in Python?

[This is in Python 3.6.8] So my impression was that a subclass inheriting from a superclass would be able to access the superclass' attributes freely, even if the attribute was "private". For example:
class A():
def __init__(self, name):
self.__name = name
def __str__(self):
return f'my name is {self.__name}'
class B(A):
def __init__(self, name, number):
A.__init__(self, name)
self.__number = number
def __str__(self):
return f'my name is {self.__name}\nmy number is {self.__number}'
def main():
name = input('name: ')
number = input('number: ')
obj = B(name, number)
print(obj)
main()
Apparently, when I try to use self.__name in the __str__ function of the subclass B, it throws an AttributeError exception, stating: AttributeError: 'B' object has no attribute '_B__name'. However, the program works as intended when I use a accessor (getter) method for getting the name instead of self.__name. So was my initial impression wrong, or am I making a misunderstanding somewhere?

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.

Change a inherited class to another inherited class keeping the attributes

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)

super and base class should have different instances of same attribute name

I'm new to python, I'm trying to learn basic inheritance and I want the super and base class to have attributes with same name, but they should have different instances. Below is the code I've written to experiment with this Idea.
class master(object):
def __init__(self, name):
self.name = name
def print_name(self):
print (self.name)
class slave(master):
def __init__(self, master_name, slave_name):
self.name = slave_name
super(slave, self).__init__(master_name)
def print_name(self):
super(slave, self).print_name()
print (self.name)
def main():
obj = slave('hello', 'world')
obj.print_name()
if __name__ == '__main__':
main()
The out put I'm getting is :
hello
hello
But I want want the output to be
world
hello
Super class and base class are different names for the same thing, in this case the master class. That is the base class for slave as well as its super class.
You could have an attribute with the same name on the classes, but not on the instance obj, which is an instance of master and slave at the same time. So self in both methods master.print_name() and slave.print_name() refers to the very same object.
An attribute on an object can only be assigned one value at a given time, so you need different names. But you can get the appearance of the same name with the name mangling that is done behind the scenes for attributes with two leading underscores:
class Master(object):
def __init__(self, name):
self.__name = name
def print_name(self):
print self.__name
class Slave(Master):
def __init__(self, master_name, slave_name):
self.__name = slave_name
Master.__init__(self, master_name)
def print_name(self):
Master.print_name(self)
print self.__name
def main():
obj = Slave('hello', 'world')
obj.print_name()
# The *actual* names of the attributes:
print obj._Master__name, obj._Slave__name
if __name__ == '__main__':
main()
The __init__ from master is overriding the value of self.name that you set in slave. You need to use different variable names.

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.

Categories