Inheritance in python, Attribute error [duplicate] - python

This question already has answers here:
What is the meaning of single and double underscore before an object name?
(18 answers)
Closed 6 years ago.
I have recently started learning python and i am having a hard time how inheritance works in python.
I have created two classes, one is named Animal and another one is named Dog. Dog class inherits Animal class. I have some attributes like name, height, sound etc in Animal class which i want to use in Dog class. I am setting the attributes using init method of Animal class.
class Animal:
__name = ""
__height = 0
__weight = 0
__sound = 0
def __init__(self, name, height, weight, sound):
self.__name = name
self.__height = height
self.__wight = weight
self.__sound = sound
def set_name(self, name):
self.__name = name
def get_name(self):
return self.__name
def set_height(self, height):
self.__height = height
def get_height(self):
return self.__height
def set_weight(self, weight):
self.__weight = weight
def get_weight(self):
return self.__weight
def set_sound(self, sound):
self.__sound = sound
def get_sound(self):
return self.__sound
def get_type(self):
print("Animal")
def tostring(self):
return "{} is {} cm tall and {} kilograms and says {}".format(self.__name,
self.__height,
self.__weight,
self.__sound)
cat = Animal("whiskers", 50, 20, "meow")
print(cat.tostring())
class Dog(Animal):
__owner = None
def __init__(self, name, height, weight, sound, owner):
super(Dog, self).__init__(name, height, weight, sound)
self.__owner = owner
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 and {} kilograms and says {} His owner is {}'.format(self.__name,
self.__height,
self.__weight,
self.__sound,
self.__owner)
def multiple_sounds(self, how_many=None):
if how_many is None:
print(self.get_sound())
else:
print(self.get_sound() * how_many)
my_dog = Dog("spot", 50, 40, "Ruff", "Derek")
print(my_dog.tostring())
Problem is when i try to print all the attributes using object of Dog class, an error is displayed saying "
*line 73, in tostring
return '{} is {} cm tall and {} kilograms and says {} His owner is {}'.format(self.__name,
AttributeError: 'Dog' object has no attribute '_Dog__name'*
Can anyone help me find the problem in this code ?

Members which start with double underscore are sort of private, so they are not accessible from cild classes. You should use single underscore. Python does not use implicit access specifiers like c++ or java but instead a special name convention is used: names with _ prefix are for protected and names with __ are for private. Also python does not check that you don't violate that convention, it is considered a bad practice to access protected or private methods outside of the class. If you start a name with double underscore it is mangled in a special way. So __name actually becomes _Animal__name when you use it in an Animal class and _Dog__name when you use it in a Dog.

The problem is the double underscore prefixes in your names. They trigger name mangling.
You should avoid using these. They don't do what you expect.

Using double underscores in Python makes a variable "private". Its similar to using the private keyword in other languages. You need to remove them if you want to access the attributes outside of the parent class.
The Python documentation touches on this subject:
Since there is a valid use-case for class-private members (namely to avoid name clashes of names with names defined by subclasses), there is limited support for such a mechanism, called name mangling. Any identifier of the form __spam (at least two leading underscores, at most one trailing underscore) is textually replaced with _classname__spam, where classname is the current class name with leading underscore(s) stripped. This mangling is done without regard to the syntactic position of the identifier, as long as it occurs within the definition of a class. (emphasis mine)

Related

Why the instance attributes in python are defined three times like this?

why is the instance attribute defined three times in python: once in the parenthesis, once in with the self keyword and once in the assignment
class Details():
def __init__(self,name,age):
self.name = name
self.age = age
__init__ is a function/method and like any other functions has "local variables" just like :
def fn(a, b):
print(a, b)
In your example, name, age in parameters and in right hand side of the assignments are just like a and b. You can choose any name you want for these variables.
But the name and age which are parts of self.name and self.age statements respectively, are attributes of your new-created instance(which self points to it). The name you choose for this attributes will exist in instance's __dict__(instance's namespace dictionary), so that's how you can call them later.
class Details:
def __init__(self, name, age):
self.name = name
self.age = age
obj = Details('John', 20)
print(obj.name)
print(obj.age)
__init__ is the constructor (in OOP terms) to create Details object. in Python, a constructor's first parameter is always self.
self points to the newly created object. It got two attributes, name and age.
the parameters, name and age inside parentheses, can have different names as well. So, the following code works the same :
class Details():
def __init__(self,n,a):
self.name = n
self.age = a

How can I make the OOP code print my classes objects name?

I've tried to make an OOP based program in python. I gave it an object to work with and tried to make it print the name, but its not working.
class human:
def __init__(self, name):
print("this is a human")
def name(self, name):
print("this is {}".format(bob.name))
bob = human("bob")
Anyone know what the problem could be?
Beyond the answers you already received (which solve your problem), I'd suggest not having a method that prints the name. Rather, you should have a __str___ dunder method that defines the object's behavior when an instance is printed.
class human:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
person = human("bob")
print(person)
'bob'
You can also define the object's behavior when the instance name is entered in the console, for instance just running the line
>>> person
You can do it with __repr__:
def __repr__(self):
return f'when entering the instance name in the console: {self.name}'
This will print:
when entering the instance name in the console: bob
This appears more pythonic to me than having a method that simply prints the name.
You're never storing the name on the instance, where would it get the name from? Your __init__ needs to do something along the lines of self.name = name
the name method and attribute are going to conflict, the latter will shadow (hide) the former, and it should look up whatever attribute its using on self
You never assigned the passed name to the object. Try:
class human:
def __init__(self, name):
print("this is a human")
self.name = name
def print_name(self):
print("this is {}".format(self.name))
bob = human("bob")
bob.print_name()
there are couple of things to update in the code:
bob is an instance which is not defined at human class
notice that init, name functions expect external param but you never use it in the function. (in self. = name)
in order to use it:
define a var in the class named 'name' and update you function to:
class human:
_name = ""
def __init__(self, name):
print("this is a human")
self._name = name
def name(self):
print("this is "+ self._name)
bob = human("bob")
bob.name()
bob = human("bob") only init function and you should call bob.name() in order to call the print-name function

Is this code correct

I have a question to ask, please. Given the code below, can you please let me know why in manager (or in the worker) class why
self.FirstName
gives the same result as
self._firstName
I would have thought that self._firstName would not be accessible in either of the classes (Manager/Worker) since it local to the Employee class and should not be accessible outside it, no ?
Please suggest.
import gc
class Employee(object):
"""Employee Base Class"""
def __init__(self, FirstName, LastName,Age, Role):
super(Employee, self).__init__()
self._firstName = FirstName
self._lastName = LastName
self._age = Age
self._role = Role
#property
def FirstName(self):
return self._firstName
#property
def Age(self):
return self._age
#property
def Role(self):
return self._role
#FirstName.setter
def FirstName(self, value):
self._firstName = value;
pass
#Role.setter
def Role(self, value):
self._role = value;
pass
class Manager(Employee):
"""Manager class"""
def __init__(self, FirstName,LastName,Age):
Employee.__init__(self,FirstName, LastName, Age, 'Manager')
# super(Manager, self).__init__()
def getParents(self):
"""Get parents of the class"""
print(gc.get_referrers(self))
pass
def ManagerInfo(self):
print("FirstName : " + self.FirstName)
print("Role : " + self.Role)
print("Age : " + str(self.Age))
class Worker(Employee):
"""docstring for Worker"""
def __init__(self, FirstName, LastName, Age):
Employee.__init__(self,FirstName, LastName, Age, 'employee')
def getParents(self):
"""Get parents of the class"""
print(gc.get_referrers(self))
pass
def WorkerInfo(self):
print("FirstName : " + self.FirstName)
print("Role : " + self.Role)
print("Age : " + str(self.Age))
pass
# manager = Employee('John','Doe' , 40, 'Manager')
# print("{0}'s age is {1} years.".format(manager.FirstName, manager.Age))
anEmp = Worker('WorkerName', 'LastName', 20)
aManager = Manager('John', 'Doe', 40)
print(anEmp.WorkerInfo())
print(anEmp.getParents())
print("----------------------------")
print(aManager.ManagerInfo())
print(aManager.getParents())
Thanks
why self.FirstName gives the same result as self._firstName
Because you defined FirstName as a property returning self._firstname. What did you expect actually ?
I would have thought that self._firstName would not be accessible in either of the classes (Manager/Worker) since it local to the Employee class
It's not 'local to the Employee class', it's an attribute of Employee instances (it doesn't exist in the Employee class itself).
and should not be accessible outside it, no ?
While prefixing a name with a single underscore denotes an implementation attribute (IOW something that is NOT part of the public API - the equivalent of 'protected' in most mainstream languages), it doesn't prevent access to the attribute. Actually there's absolutely NO enforcement of access restriction in Python, it's all convention (and eventually name mangling for __pseudoprivates names).
Python's philosophy is that we are all consenting adults and are wise enough to not do stupid things like messing with what is clearly labelled as an implementation attribute without accepting full responsability for breaking encapsulation.
can you please let me know what I should be doing in order to make sure that the user can only set the value using the setters and not by doing self._firstName
Nothing more than you already did actually. Re-read the above paragraphs, I already mentionned that Python did NOT enforced access restriction of any kind. self._firstname is prefixed with a single leading underscore, which is the way to tell "this is an implemention detail and not part of the API, you should not be messing with this attribute, you should not even know it exists, so if you break something by messing with it well too bad for you dude, but you're on your own".
so if in case, I have some arbitrary logic that manipulates the value in the setter before setting it, the updated value will not be available if the user just does self._firstName instead of self.FirstName
The chances this would happen are rather low actually (and that's an understatement) but theoritically yes this could happen. But this is totally unrelated since you'd have the very same problem if the user used self.FirstName instead since it would still return the stale value...

class and methods in python. introduction in oop

class Person:
def __init__(self, name):
"""Make a new person with the given name."""
self.myname = name
def introduction(myname):
"""Returns an introduction for this person."""
return "Hi, my name is {}.".format(myname)
# Use the class to introduce Mark and Steve
mark = Person("Mark")
steve = Person("Steve")
print(mark.introduction())
print(steve.introduction())
its suppose to produce
"Hi, my name is Mark." or "Hi, my name is Steve."
but instead it produces
"Hi, my name is undefined."
It should be printing the object's representation in memory (something along the lines of Hi, my name is <__main__.Person object at 0x005CEA10>).
The reason is that the first argument of a method is expected to be the object that the method is called upon.
Just like you have def __init__(self, name): you should have def introduction(self, myname):.
Then you will encounter another problem, as introduction now expects an argument myname which you don't provide. However, it is not needed now since you have access to self.myname.
class Person:
def __init__(self, name):
"""Make a new person with the given name."""
self.myname = name
def introduction(self):
"""Returns an introduction for this person."""
return "Hi, my name is {}.".format(self.myname)
# Use the class to introduce Mark and Steve
mark = Person("Mark")
steve = Person("Steve")
print(mark.introduction())
print(steve.introduction())
Will output
Hi, my name is Mark.
Hi, my name is Steve.
You need to declare introduction() -> introduction(self) as an instance method (by passing in self) to be able to access the instance variable self.myname.
class Person:
def __init__(self, name):
"""Make a new person with the given name."""
self.myname = name
def introduction(self):
"""Returns an introduction for this person."""
return "Hi, my name is {}.".format(self.myname)
Sample output:
# Use the class to introduce Mark and Steve
mark = Person("Mark")
steve = Person("Steve")
print(mark.introduction())
print(steve.introduction())
>>> Hi, my name is Mark.
>>> Hi, my name
Please note however, that the first parameter in a function within a class is reserved for either a class, or object to pass itself to (unless a #staticmethod tag is applied to the method, then the first implicit parameter is not passed; which essentially behave as module methods).
Also keep in mind that self is not a reserved word, so you could name it anything (even though self is PEP convention). The below example executes the same output as the example above, and is semantically the same.
def introduction(myname):
"""Returns an introduction for this person."""
return "Hi, my name is {}.".format(myname.myname)
9.3.5. Class and Instance Variables
Your problem is that your giving your introduction method the parameter myname, but never supplying it with a valid argument.You can simply do:
mark = Person(" Mark")
steve = Person(" Steve")
print(mark.introduction(mark.myname))
print(steve.introduction(steve.myname))
your giving the introduction method, the variable from your class myname.
But the above is not even necessary. Since your initializing your name variable in the __init__ method of your class, it is like a global variable. So you can simply say:
class Person:
def __init__(self, name):
"""Make a new person with the given name."""
self.myname = name
def introduction(self):
"""Returns an introduction for this person."""
return "Hi, my name is{}".format(self.myname)
# Use the class to introduce Mark and Steve
mark = Person(" Mark")
steve = Person(" Steve")
print(mark.introduction())
print(steve.introduction())

__init__ and the setting of class variables

Im having some trouble understanding Inheritance in classes and wondering why this bit of python code is not working, can anyone walk me through what is going wrong here?
## Animal is-a object
class Animal(object):
def __init__(self, name, sound):
self.implimented = False
self.name = name
self.sound = sound
def speak(self):
if self.implimented == True:
print "Sound: ", self.sound
def animal_name(self):
if self.implimented == True:
print "Name: ", self.name
## Dog is-a Animal
class Dog(Animal):
def __init__(self):
self.implimented = True
name = "Dog"
sound = "Woof"
mark = Dog(Animal)
mark.animal_name()
mark.speak()
This is the output through the terminal
Traceback (most recent call last):
File "/private/var/folders/nd/4r8kqczj19j1yk8n59f1pmp80000gn/T/Cleanup At Startup/ex41-376235301.968.py", line 26, in <module>
mark = Dog(Animal)
TypeError: __init__() takes exactly 1 argument (2 given)
logout
I was trying to get animal to check if an animal was implemented, and then if so, get the classes inheriting from animal to set the variables that Animals would then be able to manipulate.
katrielalex answered your question pretty well, but I'd also like to point out that your classes are somewhat poorly - if not incorrectly - coded. There seems to be few misunderstandings about the way you use classes.
First, I would recommend reading the Python docs to get the basic idea: http://docs.python.org/2/tutorial/classes.html
To create a class, you simply do
class Animal:
def __init__(self, name, sound): # class constructor
self.name = name
self.sound = sound
And now you can create name objects by calling a1 = Animal("Leo The Lion", "Rawr") or so.
To inherit a class, you do:
# Define superclass (Animal) already in the class definition
class Dog(Animal):
# Subclasses can take additional parameters, such as age
def __init__(self, age):
# Use super class' (Animal's) __init__ method to initialize name and sound
# You don't define them separately in the Dog section
super(Dog, self).__init__("Dog", "Woof")
# Normally define attributes that don't belong to super class
self.age = age
And now you can create a simple Dog object by saying d1 = Dog(18) and you don't need to use d1 = Dog(Animal), you already told the class that it's superclass is Animal at the first line class Dog(Animal):
To create an instance of a class you do
mark = Dog()
not mark = Dog(Animal).
Don't do this implimented stuff. If you want a class that you can't instantiate (i.e. you have to subclass first), do
import abc
class Animal(object):
__metaclass__ = abc.ABCMeta
def speak(self):
...
Since age in the given example is not part of the parent (or base) class, you have to implement the the function (which in a class is called method) in the class which inheritted (also known as derived class).
class Dog(Animal):
# Subclasses can take additional parameters, such as age
def __init__(self, age):
... # Implementation can be found in reaction before this one
def give_age( self ):
print self.age

Categories