How access python privet variable inside init function - python

How to override details() method in child class Doctor
.I want to override the details method to return id, name and Regno
current code gives an error
class Member:
def __init__(self, id, name):
self.__id = id
self.__name = name
def details(self):
return self.__id, self.__name
class Doctor(Member):
def __init__(self, id, name, drNumber):
super().__init__(id, name)
self.__drNumber = drNumber
def details(self):
return self.__id, self.__name, self.__regNo
doc = Doctor(1123, "Tim", "xxx5678")
print(doc.details())

You can't access the private attributes, because the name mangling adds the current class to the name, so it won't find the attribute with the parent's name.
Instead, call the parent method and add your value to the result.
class Doctor(Member):
def __init__(self, id, name, drNumber):
super().__init__(id, name)
self.__drNumber = drNumber
def details(self):
parent_details = super().details()
return parent_details + (self.__drNumber,)

Related

How Can a function act like a descriptor?

def typed_property(name, expected_type):
storage_name = '_' + name
#property
def prop(self):
return getattr(self, storage_name)
#prop.setter
def prop(self, value):
if not isinstance(value, expected_type):
raise TypeError('{} must be a {}'.format(name, expected_type))
setattr(self, storage_name, value)
return prop
class Person:
name = typed_property('name', str)
age = typed_property('age', int)
def __init__(self, name, age):
self.name = name
self.age = age
Function typed_property() acts like a descriptor. Why prop() is called when executing this code line (name = typed_property('name', str))?
I don't know what you mean by "descriptor". typed_property allows a property to call a function for additional processing. prop() is not called when executing the line you mentioned. It is called when executing self.name = name. The #prop.setter makes it so the object can respond to property calls like that.
When you call typed_property to set the value of the class properties name and age, you are really defining those to be methods to use to access the instance values self.name and self.age. This is the same as below omitting age for simplicity:
class Person:
def __init__(self, name):
self.name = name
#property
def name(self):
print("=== ACESSING")
return self.name
#name.setter
def name(self, name):
print("=== MUTATING")
self.name = name
This marks the name(self) method as the accessor for self.name, and name(self, val) as the mutator. The mutator is called whenever you try to change (mutate) the value of its assigned property, in this case self.name. This includes when you are calling it in the __init__ method. However, using the class as defined above will result in an infinite recursion because I am calling the mutator from inside the mutator. So "=== MUTATING" will be printed ending in a recursion error. So a small adjustment is needed:
class Person:
def __init__(self, name):
self._name = name
#property
def name(self):
print("=== ACCESSING")
return self._name
#name.setter
def name(self, val):
print("=== MUTATING")
self._name = val
Now that underlying property is name _name rather than name the mutator will set the value of _name rather than setting it for name and recur into itself infinitely. For example, using the class as defined above:
>>> p = Person("joshmeranda")
>>> p.name
=== ACCESSING
"joshmeranda"

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.

Can I change the default behavior of my parent class method in my subclass method?

I'm learning simple python inheritance and I want that one of my parent class method default argument is changed conditionally to one of my subclass argument value, and I don't know if this is possible.
Here is an example of what I'd like to do:
class Parent(object):
def __init__(self, name):
self.name = name
def doSomething(self, name, strict = True):
if strict:
return self.name
else:
return name
class Child(Parent):
def __init__(self, name, **kwargs):
super(Child, self).__init__(name)
if 'changeBehavior' in kwargs:
# Here is the thing:
# Can I change the default value of strict to kwargs['changeBehavior']
# in a way that when I later call doSomething(), it will behave according
# to its new default behavior?
def doSomething(self, name, strict = kwargs['changeBehavior']):
super(Child, self).doSomething(strict = kwargs['changeBehavior'])
If this can be done in this way, is there any other method to do so?
Thanks
You can use partial.
from functools import partial
class Parent(object):
def __init__(self, name):
self.name = name
def doSomething(self, name, strict=True):
print('Got strict={}'.format(strict))
if strict:
return self.name
else:
return name
class Child(Parent):
def __init__(self, name, **kwargs):
super().__init__(name)
change_behavior = kwargs.get('changeBehavior')
if change_behavior is not None:
self.doSomething = partial(self.doSomething, strict=change_behavior)
p = Parent('name')
c = Child('name', changeBehavior=False)
p.doSomething('name')
c.doSomething('name')
outputs
Got strict=True
Got strict=False

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)

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

Categories