I have the following code:
class person:
def __init__(self,fname,lname):
self.fname=fname
self.lname=lname
def printper(self):
return self.fname,self.lname
class emp:
def __init__(self,fname,lname,empno):
self.empno=empno
person.__init__(self,fname,lname)
def printper(self):
return self.empno,self.fname,self.lname
e1=emp("john","michael",20)
print(e1.printper())
# output: (20, 'john', 'michael')
I am confused - emp
does not inherit from person
, but the code still works. Why? Calling person.__init__()
in the emp.__init__()
seems to make the code work. Is that all that is necessary? Is it not actually mandatory to write class emp(person):
to have a derived class?
CodePudding user response:
Python does not care about checking types, inheritance relationships, etc. ahead of time. It only cares what happens when the code is run. This allows for what is called "duck typing":
class Employee:
def fired(self):
print("Oh no, I have to look for a new job")
class Gun:
def fired(self):
print("Bang! Did the bullet hit the target?")
for thing in [Employee(), Gun()]:
thing.fired()
It does not matter that the classes Employee
and Gun
have nothing to do with each other, and it does not matter that the purpose of fired
is completely different. Both of the objects in the list have a method that is named fired
, so the code works without error.
Similarly in your code:
class emp:
def __init__(self,fname,lname,empno):
self.empno=empno
person.__init__(self,fname,lname)
If we call person.__init__
, then that is just a function that we found inside the person
class - not a method that we looked up on an object. This is because we used a class on the left-hand side of .
, not an instance. That function will happily accept an emp
instance as the value for self
- it does not care whether that self
is a person
instance. It's a user-defined object, so there is no problem setting fname
and lname
attributes.
After that, the printper
method will have no problem finding fname
and lname
attributes on the emp
instance - because they are there.
"So why bother ever inheriting at all, then?"
Because even though this "works", it still doesn't actually make an inheritance relationship. That has some consequences:
super
does not work - the__init__
call had to say explicitly what other class to use, and the code doesn't really make sense. From an outside perspective, it's sheer coincidence that it sets the right attributes.isinstance
will not work, by default.emp
is not in the__bases__
, nor the__mro__
ofperson
.