0% found this document useful (0 votes)
3 views

08 Class II Inheritance

Uploaded by

haeinpark1376
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views

08 Class II Inheritance

Uploaded by

haeinpark1376
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 26

Lecture 08

Class II – Inheritance
inheritance, name mangling, printing objects

Prof. Hyeong-Seok Ko
Seoul National University
Dept. of Electrical and Computer Engineering
Contents
• Inheritance
• Name Mangling
• Printing the Content of a Object
Syntax of Inheritance

class Person:
def __init__(self, name, phoneNumber):
self.name = name
self.phoneNumber = phoneNumber base class
= superclass
def print_info(self):
print("Name: ", self.name, ", Phone Number: ", self.phoneNumber)

class Student(Person):
def __init__(self, name, phoneNumber, subject, studentID):
super().__init__(name, phoneNumber) derived class Person
self.subject = subject = subclass
self.studentID = studentID

Student

class hierarchy diagram


Meaning of Inheritance
• Inherits all properties of the base class

Person
class Person:
name = "Default Name"
Person ... name
def print_info(self): print_info
...

Student class Student(Person): Student


hakbun = "2024"
...
def print_grade(self): name
...
print_info

hakbun
print_grade
Reusing Superclass Properties
class Person:
def __init__(self, name, phoneNumber):
self.name = name
self.phoneNumber = phoneNumber base class
= superclass
def print_info(self):
print("Name: ", self.name, ", Phone Number: ", self.phoneNumber)

class Student(Person):
def __init__(self, name, phoneNumber, subject, studentID):
super().__init__(name, phoneNumber) derived class
self.subject = subject = subclass
self.studentID = studentID

reusing the superclass constructor


p = Person("Tom", "011-")
Copying & pasting 2 lines of code would have also worked.
s = Student("Marry", "010-", "CS", "2012")
L = [p, s] But reusing is strongly recommended.

for item in L : Name: Tom , Phone Number: 011-


item.print_info() Name: Marry , Phone Number: 010-
Programming the superclass portion is Necessary.
Without it, Superclass Member Variables are not Inherited
class Person:
def __init__(self, name, phoneNumber):
self.name = name
self.phoneNumber = phoneNumber base class
= superclass
def print_info(self):
print("Name: ", self.name, ", Phone Number: ", self.phoneNumber)

class Student(Person):
def __init__(self, name, phoneNumber, subject, studentID):
# super().__init__(name, phoneNumber) derived class
self.subject = subject = suberclass
self.studentID = studentID

p = Person("Tom", "011-") in print_info


s = Student("Marry", "010-", "CS", "2012") print("Name : ", self.name, ",
L = [p, s] Phone Number : ", self.phoneNumber)
AttributeError: 'Student' object has
for item in L : no attribute 'name'
item.print_info()
Type&Run: issubclass()

print(issubclass(Student, Person)) True


print(issubclass(Person, Student)) False
print(issubclass(Person, Person)) True
print(issubclass(Student, Student)) True
print(issubclass(Person, object)) True
print(issubclass(Student, object)) True
print(issubclass(Student, list)) False
Type&Run: isinstance(), .__class__

class Person: True


x = "Person" True
True
class Student(Person): False
x = "Student" True
<class '__main__.Student'>
s = Student()
print(isinstance(s, Student))
print(isinstance(s, Person))
print(isinstance(s, object))
print(s.__class__ == Person)
print(s.__class__ == Student)
print(s.__class__)
Type&Run: __bases__

class Person: (<class 'object'>,)


# class Person(): (<class '__main__.Person'>,)
# class Person(object): (<class 'object'>,)
species = "Homo Sapiens" (<class '__main__.Person'>,)

def __init__(self, name):


self.name = name

class Student(Person):
def __init__(self, name, major):
super().__init__(name)
self.major = major

p = Person("Kim")
s = Student("Kim", "ECE")

print(Person.__bases__)
print(Student.__bases__)
print(p.__class__.__bases__)
print(s.__class__.__bases__)
Variable Overriding
• By inheritance, the subclass inherits the variables from the superclass.
• But there are cases when the superclass variable doesn’t quite fit the subclass.
• In such a case, Python allows us to define a variable in the subclass with the same name as
the one in the superclass, which is called variable overriding.

class Person: class Person:


x = "x in Person" x = "x in Person"

class Student(Person): class Student(Person):


def print_x(self): x = "x in Student"
print(self.x) def print_x(self):
print(self.x)
s = Student()
s.print_x() s = Student()
s.print_x()
x in Person
x in Student
Similarly, Method Overriding is also Allowed
class Person: Method 1 p = Person("Tom", "011-")
def __init__(self, name, phoneNumber): s = Student("Marry", "010-", "CS", "2012")
self.name = name
L = [p, s]
self.phoneNumber = phoneNumber
def print_info(self):
print("Name: ", self.name, ", Phone Number: ", self.phoneNumber) for item in L:
class Student(Person): item.print_info()
def __init__(self, name, phoneNumber, subject, studentID):
super().__init__(name, phoneNumber)
self.subject = subject Name: Tom ,Phone Number: 011-
self.studentID = studentID Name: Marry , Phone Number : 010-
def print_info(self): Subject: CS , ID : 2012
print("Name: ", self.name, ", Phone Number: ", self.phoneNumber)
print("Subject: ", self.subject, ", ID: ", self.studentID)

class Person: Method 2


def __init__(self, name, phoneNumber):
self.name = name • Methods 1 and 2 produce the same result.
self.phoneNumber = phoneNumber • However, Method 2 is more recommended.
def print_info(self):
print("Name: ", self.name, ", Phone Number: ", self.phoneNumber) • Repeating the same code is bad because:
class Student(Person): – When you find that code contains a problem,
def __init__(self, name, phoneNumber, subject, studentID): you have to look over all occurrences of them.
super().__init__(name, phoneNumber) • Remember:
self.subject = subject
self.studentID = studentID – When coding, avoid repeating as much as
def print_info(self): possible.
super().print_info() # reusing the superclass method
print("Subject: ", self.subject, ", ID: ", self.studentID)
Type&Run: mro() # Method Resolution Order
class Person:
species = "Homo Sapiens"

def __init__(self, name):


self.name = name

class Student(Person):
def __init__(self, name, major):
super().__init__(name)
self.major = major

p = Person("Kim")
s = Student("Kim", "ECE")

print(Person.mro())
print(Student.mro())
print(p.__class__.mro())
print(s.__class__.mro())

[<class '__main__.Person'>, <class 'object'>]


[<class '__main__.Student'>, <class '__main__.Person'>, <class 'object'>]
[<class '__main__.Person'>, <class 'object'>]
[<class '__main__.Student'>, <class '__main__.Person'>, <class 'object'>]
Strategy for Class Inheritance
• Sort out common data/functionalities.
• Create a superclass, write commonly used codes there, and reuse them from the subclasses.
Name Mangling
Let’s Review Inheritance…
class Person:
def __init__(self, name, phoneNumber):
self.name = name
self.phoneNumber = phoneNumber base class
= superclass
def print_info(self):
print("Name : ", self.name, ", Phone Number : ", self.phoneNumber)

class Student(Person):
def __init__(self, name, phoneNumber, subject, studentID):
super().__init__(name, phoneNumber) derived class
self.subject = subject = suberclass
self.studentID = studentID

p = Person("Tom", "011-")
s = Student("Marry", "010-", "CS", "2012")
l = [p, s]

for item in l : Name : Tom , Phone Number : 011-


item.print_info() Name : Marry , Phone Number : 010-
Without super()…

class Person:
def __init__(self, name, phoneNumber):
self.name = name
self.phoneNumber = phoneNumber base class
= superclass
def print_info(self):
print("Name : ", self.name, ", Phone Number : ", self.phoneNumber)

class Student(Person):
def __init__(self, name, phoneNumber, subject, studentID):
# super().__init__(name, phoneNumber) derived class
self.subject = subject = suberclass
self.studentID = studentID

p = Person("Tom", "011-") in print_info


s = Student("Marry", "010-", "CS", "2012") print("Name : ", self.name, ",
l = [p, s] Phone Number : ", self.phoneNumber)
AttributeError: 'Student' object has
for item in l : no attribute 'name'
item.print_info()
Without super()…

class Person:
def __init__(self, name, phoneNumber): p = Person("Tom", "011-")
self.name = name s = Student("Marry", "010-", "CS", "2012")
self.phoneNumber = phoneNumber l = [p, s]
def print_info(self): for item in l :
print("Name : ", self.name, ", Phone Number : ", self.phoneNumber) print(dir(item))
class Student(Person):
def __init__(self, name, phoneNumber, subject, studentID):
# super().__init__(name, phoneNumber)
self.subject = subject
self.studentID = studentID
There is no 'name' or 'phoneNumber' for s.

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',


'__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__',
'__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__', 'name', 'phoneNumber', 'print_info']

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',


'__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__',
'__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__', 'print_info', 'studentID', 'subject']
Experiment 1
Can we avoid that?
class A: class A: class A: class A:
def __init__(self): def __init__(self): def __init__(self): def __init__(self):
self.x = 3 self.x = 3 self.x = 3 self.x = 3
def Print(self): def Print(self): def Print(self): def Print(self):
print(self.x) print(self.x) print(self.x) print(self.x)

class B(A): class B(A): class B(A): class B(A):


def __init__(self): def __init__(self): def __init__(self): def __init__(self):
super().__init__() print(self.x) // L1 super().__init__() super().__init__()
self.p = 0 self.p = 0 print(self.x) self.p = 0
def Print(self): self.x = 0 self.p = 0 self.x = 0
print(self.p, self.x) def Print(self): self.x = 0 def Print(self):
print(self.p, self.x) def Print(self): print(self.p, self.x, ?)
b = B() print(self.p, self.x)
b.Print() b = B() b = B()
b.Print() b = B() b.Print()
b.Print()
0 3
L1 produces an error 0 0 3 Class B’s x
3
0 0 Class A’s x

Class B’s x overrides class A’s x. How?


That’s Why Python has Name Mangling…
a naïve try If you use name mangling, it works!
class A: class A:
def __init__(self): def __init__(self):
self.x = 3 self.__x = 3
def Print(self): def Print(self):
print(self.x) print(self.__x)

class B(A): class B(A):


def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.p = 0 self.p = 0
self.x = 0 self.x = 0
def Print(self): def Print(self):
print(self.p, self.x, self.A.x) print(self.p, self.x, self._A__x)

b = B() b = B()
b.Print() b.Print()
print(dir(b))

print(self.p, self.x, self.A.x)


AttributeError: 'B' object has 0 0 3
no attribute 'A' ['_A__x',..., 'p', 'x']

A’ x B’s x
More about Name Mangling
• A double underscore prefix causes the Python interpreter to rewrite the attribute name in order to avoid
naming conflicts in subclasses.
• This is called name mangling—the interpreter changes the name of the variable in a way that makes it
harder to create collisions when the class is extended later.
• In Experiment 2, in the attribute list of t, both foo and _bar appear but __baz does not.
– Instead, there is an attribute called _X__baz.
– This is the name mangling that Python applies, in order to protect the variable from getting overridden in the
subclasses.
– Note that, within the class definition, __baz can be freely used.
– Outside the class definition, however, it must be referred with _X__baz.
• In Experiment 3, note that the original _X__baz is also still around.

Experiment 2
class X: Experiment 3
def __init__(self): class Y(X): >>> t2 = Y()
self.foo = 11 def __init__(self): >>> t2._bar
self._bar = 22 super().__init__() 222
self.__baz = 33 self.foo = 111 >>> t2.__baz
self._bar = 222 AttributeError: "'Y' object has no attribute '__baz'“
>>> t = X() self.__baz = 333 >>> dir(t2)
>>> dir(t) ['_Y__baz', '_X__baz',..., '_bar', 'foo']
['_X__baz',..., '_bar', 'foo'] >>> t2._Y__baz
>>> print(t._X__baz) 333
33 >>> t2._X__baz
>>> print(t.__baz) 33
error
Printing the Content of a Object
Often you want to see the content of objects

class Person :
name = "Default Name "
year_born = 1996

p1 = Person(); p1.name = "Steve"; p1.year_born = 2000


p2 = Person(); p2.name = "In-Soo"; p2.year_born = 1983

print(p1)
print(p2)

<__main__.Person object at 0x000001A688F77850>


<__main__.Person object at 0x000001A68B7E9F90>

This is not the real content…


Printing an Object: This is the way…
class Person : Here is a person:
name = "Default Name " Name = Steve
year_born = 1996 Year Born 2000
def __str__(self):
return "Here is a person: \n" + \ Here is a person:
" Name = %s\n" % self.name + \ Name = Steve
" Year Born %d\n" % self.year_born Year Born 2000

p1 = Person(); p1.name = "Steve"; p1.year_born = 2000 Here is a person:


p2 = Person(); p2.name = "In-Soo"; p2.year_born = 1983 Name = Steve
Year Born 2000
# The driver program to test above...
print(p1) # __str__() is called
print(p1.__str__()) # same as above • If you think you will need to dump the
z = str(p1) # internally calls __str__() object content, create the __str__()
print(z) function.
• __str__() is what is called when you
print an object p1 with print(p1).
• If you call str(p1) with an object p1, in
fact, it internally calls __str__().
Class Exercise:
• Define the __str__() function below.

class Complex:
def __init__(self, real, imag):
self.real = real
self.imag = imag

def __str__(self): 10 + i20


# define this line yourself...
# the driver to test the above
c = Complex(10, 20)
print(c)
Reusing __str__() of Other Classes
class Vector3 :
def __init__(self, *args):
if len(args) == 0 :
self.x = self.y = self.z = 0
elif len(args) == 3 :
x,y,z = args
self.x = x; self.y = y; self.z = z

def __str__(self):
return '(' + str(self.x) + ' ' + str(self.y) + ' ' + str(self.z) + ')'

class Sphere :
def __init__(self, center, radius):
self.center = center
self.radius = radius

def __str__(self):
return "Center : " + str(self.center) + " , Radius : " + str(self.radius)

center = Vector3(1,2,3)
sphere = Sphere(center, 10)
print(sphere)

Center : (1 2 3) , Radius : 10
built-in
Lecture 08
Class II – Inheritance
inheritance, name mangling, printing objects

The End

You might also like