[go: up one dir, main page]

0% found this document useful (0 votes)
16 views26 pages

08 Class II Inheritance

Uploaded by

haeinpark1376
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
16 views26 pages

08 Class II Inheritance

Uploaded by

haeinpark1376
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
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