Why do I keep getting this Attribute Error? - python

Every time I run my code it pops up the message saying "'ICU' object has no attribute '_name'. Did you mean: 'name'?" I can not figure out how to fix it. I've tried changing the name of the accessors and mutators but still can't figure out how to solve it. Any suggestions?
Here's my code:
class Patient:
def __init__(self, name, age):
self.name = name
self.age = age
self.weight = 150
#property
def age(self):
return self._age
#age.setter
def age(self, newValue):
if newValue > 0:
self._age = newValue
else:
self._age = 0
#property
def weight(self):
return self._weight
#weight.setter
def weight(self, newValue):
if newValue >=0 and newValue <= 1400:
self._weight = newValue
#IncreaseAge
def increaseAge(self):
self.age = self.age + 1
class In(Patient):
def __init__(self, name, age, stay):
self.name = name
self.age = age
self.stay = stay
#property
def stay(self):
return self._stay
#stay.setter
def stay(self, value):
self._name = value
def __str__(self):
print("IN-" + self._name + self._age + self.weight + self._stay)
class Out(Patient):
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
print("OUT-" + self._name + self._age + self._weight)
class ICU(In):
def __init__(self, name, age):
self.name = name
self.age = age
self.days = 5
class CheckUp(Out):
def __init__(self, name, age):
self.name = name
self.age = age
Here's the rest of the instance:
# Create three patient objects and print them out
p1 = ICU("Ben Dover", 0)
p2 = ICU("Helen Hywater", -15)
p3 = CheckUp("Amanda Lynn", 45)
p4 = ICU("Chester Minit", 12)
p5 = In("Don Keigh", 89, 10)
p6 = Out("Kay Oss ", 45)
print ("\tStatus\tName\t\tAge\tWeight\tStay")
print ("-" * 55)
print ("p1:\t{}".format(p1))
print ("p2:\t{}".format(p2))
print ("p3:\t{}".format(p3))
print ("p4:\t{}".format(p4))
print ("p5:\t{}".format(p5))
print ("p6:\t{}".format(p6))
print ("-" * 55)
# Change their ages and print them out
p1.age = -5
p2.age = 100
for i in range(6):
p3.increaseAge()
p4.age = 0
p5.increaseAge()
p6.age = 42
print ("p1:\t{}".format(p1))
print ("p2:\t{}".format(p2))
print ("p3:\t{}".format(p3))
print ("p4:\t{}".format(p4))
print ("p5:\t{}".format(p5))
print ("p6:\t{}".format(p6))
print ("-" * 55)
# Change other instance variables and print them out
p1.weight = 2000
p1.stay = 3
p2.name = "Justin Thyme"
p2.weight = 220
p2.stay = 0
p3.weight = -50
p4.weight = 1400
p5.weight = 0
p5.stay = 21
p6.weight = 1401
print ("p1:\t{}".format(p1))
print ("p2:\t{}".format(p2))
print ("p3:\t{}".format(p3))
print ("p4:\t{}".format(p4))
print ("p5:\t{}".format(p5))
print ("p6:\t{}".format(p6))
print ("-" * 55)

It's because your variable name is different.
Replace you code from:
#stay.setter
def stay(self, value):
self._name = value
To:
#stay.setter
def stay(self, value):
self.name = value

In Python, constructors - like all other methods - can be overridden. That is once you define __init__ in child classes, the base class method is never called. This is what's causing the error.
You need to explicitly call the base class like like this:
class ICU(In):
def __init__(self, name, age):
self.name = name
self.age = age
self.days = 5
In.__init__(self, name, age, 10) # stay = 10 since it's not an input parameter in the ICU __init__ method.
This needs to be done in every base class. So you'd do something similar in the In class as well.

The problem comme to the fact that "format" is calling "__ str__" on your instances but when "__ str__" get called, some of your instance doesn't have a value for "_name" or "_stay" or "_weight"...see your " __ init __ " method for each instance and execute " __ str __" after you will see the problem. so to handle this case you have the following simple solution
class In(Patient):
def __init__(self, name, age, stay):
self.name = name
self.age = age
self.stay = stay
#property
def stay(self):
return self._stay
#stay.setter
def stay(self, value):
self._name = value
def __str__(self):
x = (
getattr(self, '_name', ''),
getattr(self, '_age', ''),
self.weight or ''
getattr(self, '_stay', ''),
)
return ("IN-%s %s %s %s")%(*x)
class Out(Patient):
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
x = (
getattr(self, '_name', ''),
getattr(self, '_age', ''),
getattr(self, '_stay', ''),
)
return "OUT- %s %s %s"%(*x)
But your classes are not well designed, see below something interesting
class Patient:
def __init__(self, name, age, weight=150):
self._name= name
self._age = age
self._weight = weight
#property
def name(self):
return self._name
#name.setter
def name(self, value):
assert isinstance(value, str)
self._name = value
#property
def age(self):
return self._age
#age.setter
def age(self, value):
assert isinstance(value, int)
self._age = value
#property
def weight(self):
return self._weight
#weight.setter
def weight(self, value):
assert isinstance(value, int)
self._weight = value
def __str__(self):
return f"{self.__class__.__name__.upper()}-{self.name} {self.age} {self.weight}"
class Out(Patient):
pass
class In(Patient):
def __init__(self, name, age, stay, weight=150):
super().__init__(name, age, weight=weight)
self._stay = stay
#property
def stay(self):
return self._stay
#stay.setter
def stay(self, value):
assert isinstance(value, int)
self._stay = value
def __str__(self):
return f"{super().__str__()} {self.stay}"
class ICU(In):
def __init__(self, name, age):
super().__init__(name, age, 5)
class CheckUp(Out):
def __init__(self, name, age):
super().__init__(name, age)
Also note that "increaseAge" method is not defined on your instances

Related

Achieving single-responsibility principle with python abstract classes

I want to separate the DB models from the actual classes. But i need two static functions for fetching data from the DB regardless of the subclass type. the implementation for both functions are the same across all DB models.
pyright showing an error that cls inside get() and get_all() functions doesn't have a db property.
from abc import ABC, abstractstaticmethod
class DogsDB:
lists = ["DOG1", "DOG2", "DOG3"]
#classmethod
def get(cls, id):
return cls.lists[id]
class CatsDB:
lists = ["CAT1", "CAT2", "CAT3"]
#classmethod
def get(cls, id):
return cls.lists[id]
class Animal(ABC):
def __init__(self, name):
self.name = name
#abstractstaticmethod
def save(m):
pass
#abstractstaticmethod
def _from_model(obj):
pass
#classmethod
def get(cls, id):
obj = cls.db.get(id)
return cls._from_model(obj)
#classmethod
def get_all(cls):
objs = cls.db.lists
lists = []
for obj in objs:
e = cls._from_model(obj)
lists.append(e)
return lists
def __repr__(self):
return self.name
class DogSound:
def __init__(self, name):
self.name = name
def sound(self):
print(self.name, ": DOG SOUND!!")
class Dog(Animal, DogSound):
db = DogsDB
def __init__(self, name, age):
super(Dog, self).__init__(name)
self.age = age
#staticmethod
def save(m):
print(m)
#staticmethod
def _from_model(obj):
return Dog(obj, 4)
class Cat(Animal):
db = CatsDB
def __init__(self, name, age):
super().__init__(name)
self.age = age
#staticmethod
def save(m):
print(m)
#staticmethod
def _from_model(obj):
return Cat(obj, 4)
print(Cat.get(1))
print(Dog.get(1))
print(Cat.get_all())
print(Dog.get_all())
Dog.get(1).sound()
I cannot duplicate your first error.
Your second issue is a result of method sound implicitly returning None since it has no return statement and you have print(Dog.get(1).sound()), which will print out the return value from that method. You either want to change this to just Dog.get(1).sound() or modify the sound method to return what it is currently being printed and remove the print statement (my choice).
As an aside, I found this class structure a bit difficult to follow. Why do you need a separate DogSound class with a name attribute which should belong to Animal? Also, it seems to me that age could/should be an attribute of Animal since both cats and dogs have an age.
from abc import ABC, abstractstaticmethod
class DogsDB:
lists = ["DOG1", "DOG2", "DOG3"]
#classmethod
def get(cls, id):
return cls.lists[id]
class CatsDB:
lists = ["CAT1", "CAT2", "CAT3"]
#classmethod
def get(cls, id):
return cls.lists[id]
class Animal(ABC):
def __init__(self, name, age):
self.name = name
self.age = age
#abstractstaticmethod
def save(m):
pass
#abstractstaticmethod
def _from_model(obj):
pass
#classmethod
def get(cls, id):
obj = cls.db.get(id)
return cls._from_model(obj)
#classmethod
def get_all(cls):
objs = cls.db.lists
lists = []
for obj in objs:
e = cls._from_model(obj)
lists.append(e)
return lists
def __repr__(self):
return self.name
class Dog(Animal):
db = DogsDB
def __init__(self, name, age):
super().__init__(name, age)
def sound(self):
return f"{self.name}: DOG SOUND!!"
#staticmethod
def save(m):
print(m)
#staticmethod
def _from_model(obj):
return Dog(obj, 4)
class Cat(Animal):
db = CatsDB
def __init__(self, name, age):
super().__init__(name, age)
self.age = age
#staticmethod
def save(m):
print(m)
#staticmethod
def _from_model(obj):
return Cat(obj, 4)
print(Cat.get(1))
print(Dog.get(1))
print(Cat.get_all())
print(Dog.get_all())
print(Dog.get(1).sound())
Prints:
CAT2
DOG2
[CAT1, CAT2, CAT3]
[DOG1, DOG2, DOG3]
DOG2: DOG SOUND!!
If for some reason you want DogSound to be a separate class, then there is no need for the name attribute to be duplicated:
...
class DogSound: # A "Mixin" class
def sound(self):
return f"{self.name}: DOG SOUND!!"
class Dog(Animal, DogSound):
db = DogsDB
def __init__(self, name, age):
super().__init__(name, age)
#staticmethod
def save(m):
print(m)
#staticmethod
def _from_model(obj):
return Dog(obj, 4)
...

How '#decorator" actually works in python?

How '#decorator' actually works?
I just knew it works like this:
def deco(func):...
#deco
def func():...
# same
def func():...
func = deco(func)
But it doesn't work well in '#class.method'
Below is an example:
class Person:
def __init__(self, name):
self._name = name
#property
def name(self):
return self._name
#name.setter
def name(self.value):
self._name = value
class Person:
def __init__(self, name):
self._name = name
## #propery
def name(self):
return self._name
name = property(name)
## #name.setter
def name(self, value):
self._name = value
name = name.setter(name) <----- error
The error here is because name has been changed to point to "def name(self.value):..."
I'd like to know how '#property.method' actually works.
I just guessed like that:
class Person:
def __init__(self, name):
self._name = name
## #propery
def name(self):
return self._name
name = property(name)
## #name.setter
x = name.setter
def name(self, value):
self._name = value
name = x.setter(name)
del x
Is it right or is there other way to work that I don't know?
Thank you for reading!

It is not printing from Child Class

There is super class with the name Employee has the following fields.
empNum, name, address and DOB.
There are two child class or sub class of Employee class. They are FullTimeEmployee and PartTimeEmployee.
FullTimeEmployee has monthlySalary (other than the fields in Super Class)
PartiTime Employee has hourlyRate (other than the fields in Super Class)
Here is a code that i have written in Python.
However when i print it is only printing the fields in the superclass.
May i know which part im doing wrong.
class Employee:
def __init__(self, **kwargs):
if 'empNum' in kwargs:self._empNum=kwargs['empNum']
if 'name' in kwargs:self._name=kwargs['name']
if 'address' in kwargs:self._address=kwargs['address']
if 'dob' in kwargs:self._dob=kwargs['dob']
def empNum(self,en=None):
if en:self._empNum=en
return self._empNum
def name(self,nme=None):
if nme:self._name=nme
return self._name
def address(self,addr=None):
if addr:self._address=addr
return self._address
def dob(self,db=None):
if db:self._dob=db
return self._name
def __str__(self):
return self._empNum +","+ self._name+","+self._dob+","+self._address
#child class
class FullTimeEmployee(Employee):
def __init__(self, **kwargs):
super().__init__( **kwargs)
if 'monthlySalary' in kwargs:self._monthlySalary=kwargs['monthlySalary']
def monthlySalary(self, s=None):
if s:self._monthlySalary=s
return self._monthlySalary
def _str_(self):
return super().__init__ + "," + self._monthlySalary
#child class
class PartTimeEmployee(Employee):
def __init__(self, **kwargs):
super().__init__( **kwargs)
if 'hourlyRate' in kwargs:self._hourlyRate=kwargs['hourlyRate']
def hourlyRate(self, hr=None):
if hr:self._hourlyRate=hr
return self._hourlyRate
def _str_(self):
return str(super()._str_()) +"," + self._hourlyRate
def main():
records = []
test = []
emp = Employee(empNum='100', name="Ibrahim Shathir", address="H. North Pole,Male', Maldives", dob="16-05-1989")
records.append(emp)
emp1 = PartTimeEmployee(empNum='101', name="Mohamed Latheef", address="H. North Pole,Male', Maldives", dob="16-05-1989", hourlyRate="150")
records.append(emp1)
emp2 = FullTimeEmployee(empNum='102', name="Shujau Ibrahim", address="H. North Pole,Male', Maldives", dob="16-05-1989", monthlySalary="12500")
records.append(emp2)
print(records[0])
for emps in records:
print(emps)
print(emp2)
if __name__ == '__main__':main()
[1]: https://i.stack.imgur.com/7pzph.jpg
[2]: https://i.stack.imgur.com/JbOHv.jpg
Please override __str__ instead of _str_ in subclass. The following code should work.
class Employee:
def __init__(self, **kwargs):
if 'empNum' in kwargs:self._empNum=kwargs['empNum']
if 'name' in kwargs:self._name=kwargs['name']
if 'address' in kwargs:self._address=kwargs['address']
if 'dob' in kwargs:self._dob=kwargs['dob']
def empNum(self,en=None):
if en:self._empNum=en
return self._empNum
def name(self,nme=None):
if nme:self._name=nme
return self._name
def address(self,addr=None):
if addr:self._address=addr
return self._address
def dob(self,db=None):
if db:self._dob=db
return self._name
def __str__(self):
return self._empNum +","+ self._name+","+self._dob+","+self._address
#child class
class FullTimeEmployee(Employee):
def __init__(self, **kwargs):
super().__init__( **kwargs)
if 'monthlySalary' in kwargs:self._monthlySalary=kwargs['monthlySalary']
def monthlySalary(self, s=None):
if s:self._monthlySalary=s
return self._monthlySalary
def __str__(self):
prefix = super().__str__()
return '{}, {}'.format(prefix, self._monthlySalary)
#child class
class PartTimeEmployee(Employee):
def __init__(self, **kwargs):
super().__init__( **kwargs)
if 'hourlyRate' in kwargs:self._hourlyRate=kwargs['hourlyRate']
def hourlyRate(self, hr=None):
if hr:self._hourlyRate=hr
return self._hourlyRate
def __str__(self):
prefix = super().__str__()
return '{}, {}'.format(prefix, self._hourlyRate)
def main():
records = []
test = []
emp = Employee(empNum='100', name="Ibrahim Shathir", address="H. North Pole,Male', Maldives", dob="16-05-1989")
records.append(emp)
emp1 = PartTimeEmployee(empNum='101', name="Mohamed Latheef", address="H. North Pole,Male', Maldives", dob="16-05-1989", hourlyRate="150")
records.append(emp1)
emp2 = FullTimeEmployee(empNum='102', name="Shujau Ibrahim", address="H. North Pole,Male', Maldives", dob="16-05-1989", monthlySalary="12500")
records.append(emp2)
print(records[0])
for emps in records:
print(emps)
print(emp2)
if __name__ == '__main__':main()

Python - TypeError: module.__init__() takes at most 2 arguments (3 given)

Trying my hand at Python inheritance. I need your help on how to fix an error.
I have 2 classes: Person (super class )& Contact (sub class).
I get the following error when trying to run Contact:
"Contact.py", line 3, in <module>
class Contact(Person):
TypeError: module.__init__() takes at most 2 arguments (3 given)
Thanks in advance
Below is my code:
class Person:
__name=""
__age=0
def __init__(self, name, age):
self.__name = name
self.__age = age
def set_name(self, name):
self.__name = name
def set_age(selfself, age):
self.__age = age
def get_name(self):
return self.__name
def get_age(selfself):
return self.__age
def getInfo(self):
return "Name is: {} - Age is: {}".format(self.__name, self.__age)
# ----------------------------------------------------
import Person
class Contact(Person):
__method=""
def __init__(self, name, age, method):
super().__init__(name, age)
self.__method = method
def set_method(self, method):
self.__method = method
def get__method(self):
return self.__method
def getInfo(self):
return "Name is: {} - Age is: {} - Contact Info: {}".format(self.__name, self.__age, self.__method)
person2 = Contact("Adam Smith", 19, "Email: adam.smith#abcde.net")
print(person2.getInfo())
First of all, the indentation is messed up!
If Person is in a separate file, import the file name without extension, like this:
class Person:
def __init__(self, name, age):
self.__name = name
self.__age = age
def set_name(self, name):
self.__name = name
def set_age(self, age):
self.__age = age
def get_name(self):
return self.__name
def get_age(self):
return self.__age
def getInfo(self):
return "Name is: {} - Age is: {}".format(self.__name, self.__age)
# ----------------------------------------------------
from person import Person # assumed your Person class is in person.py
class Contact(Person):
__method=""
def __init__(self, name, age, method):
super().__init__(name, age)
self.__method = method
def set_method(self, method):
self.__method = method
def get__method(self):
return self.__method
def getInfo(self):
return "Name is: {} - Age is: {} - Contact Info: {}".format(self.get_name(), self.get_age(), self.__method)
person2 = Contact("Adam Smith", 19, "Email: adam.smith#abcde.net")
print(person2.getInfo())
Access the parent class's private fields through its methods.

AttributeError: 'Dog' object has no attribute 'get_height'

Should you need it, the tutorial I am following is Python Programming by #Derek Banas:
This lesson is demonstrating class object inheritance
class Animal:
__name = ""
__height = 0
__weight = 0
__sound = 0
def __init__(self, name, height, weight, sound):
self.__name = name
self.__height = height
self.__weight = weight
self.__sound = sound
def set_name(self, name):
self.__name = name
def get_name(self):
return self.__name
def get_type(self):
print("Animal")
def toString(self):
return "{} is {} cm tall and {} kilograms and {}".format(self.__name,
self.__height,
self.__weight,
self.__sound)
cat = Animal('Whiskers', 33, 10, 'Meow')
print(cat.toString())
class Dog(Animal):
__owner = ""
def __init__(self, name, height, weight, sound, owner):
self.__owner = owner
super(Dog, self).__init__(name, height, weight, sound)
def set_owner(self, owner):
self.__owner = owner
def get_owner(self):
return self.__owner
def get_type(self):
print("Dog")
def toString(self):
return "{} is {} cm tall & {} kgrms and {} hi
{}".format(self.get_name(),
self.get_height(),
self.get_weight(),
self.get_sound(),
self.get_owner())
""" I am getting this runtime error message python version 3.6
Here is the error:
File "C:/Watson/HDM/tutorial_py1.py", line 192, in toString
self.get_height(),
AttributeError: 'Dog' object has no attribute 'get_height' """
Dog object is not having any attribute 'get_height' as get_height() function is not declared in the class before. You need to add the method in the class:
def get_height(self, name):
return self.__height

Categories