การสืบทอด(Inheritance)
การสืบทอดเป็นคุณลักษณะเด่นอย่างหนึ่งสำหรับการเขียนโปรแกรมแบบ OO(Object-Oriented Programming) โดยอ้างถึงการกำหนดคลาสขึ้นมาใหม่และด้วยคุณลักษณะจากคลาสที่มีอยู่โดยไม่มีการแก้ไขใดๆ โดยคลาสใหม่เรียกว่าคลาสสืบทอด(derived or child class) และคลาสที่เป็นต้นแบบของการสืบทอดนั้นเรียกว่าคลาส base(หรือ parent)
class BaseClass:
# รายละเอียดของ Base Class
class DerivedClass(BaseClass):
# รายละเอียดของ Derived Class
คลาสสืบทอด(derived class) ได้สืบทอดคุณลักษณะจากคลาสต้นแบบ และสามารถเพิ่มคุณลักษณะใหม่ลงไปได้ ซึ่งทำให้เราสามารถสร้างคลาสใหม่โดยการนำคุณลักษณะเดิมที่มีอยู่แล้วมาปรับปรุงเพื่อใช้งานได้มากขึ้น
ตัวอย่างการสืบทอด
กำหนดให้ polygon เป็นรูปปิดซึ่งมีด้าง 3 ด้าน เราทำการสร้างคลาส polygon ได้ดังนี้
class Polygon:
def __init__(self, no_of_sides):
self.n = no_of_sides
self.sides = [0 for i in range(no_of_sides)]
def inputSides(self):
self.sides = [float(input("Enter side "+str(i+1)+" : ")) for i in range(self.n)]
def dispSides(self):
for i in range(self.n):
print("Side",i+1,"is",self.sides[i])
โดยคลาสจะมีข้อมูลของแอทริบิวท์เพื่อเก็บจำนวนของด้านโดย n และ magnitude ของแต่ละด้านทำการเก็บข้อมุลในลิสต์ชื่อ sides
ฟังก์ชัน InputSides() เราเอาข้อมูล magnitude ของแต่ละด้านและฟังก์ชัน dispSides() จะทำการแสดงข้อมูลดังกล่าว
โดยสามเหลี่ยมก็เป็นรูปแบบหนึ่งของ polygon ซึ่งประกอบด้วยข้อมูลของด้าน 3 ด้าน ดังนั้นเราสามารถจะสร้างคลาสชื่อว่า Triangle ซึ่งสืบทอดคุณลักษณะจาก polygon ซึ่งจะทำให้แอทริบิวท์ทั้งหมดที่มีในคลาส Polygon สามารถจะใช้ได้ในคลาส Triangle โดยไม่ต้องเขียนโค้ดใหม่เพื่อระบุ
class Triangle(Polygon):
def __init__(self):
Polygon.__init__(self,3)
def findArea(self):
a, b, c = self.sides
# calculate the semi-perimeter
s = (a + b + c) / 2
area = (s*(s-a)*(s-b)*(s-c)) ** 0.5
print('The area of the triangle is %0.2f' %area)
ในคลาส Triangle เราได้ทำการเพิ่มฟังก์ชันใหม่คือ findArea() เพื่อจะทำการคำนวณและพิมพ์ค่าขนาดของพื้นที่ของสามเหลี่ยม
t = Triangle() t.inputSides() #Enter side 1: 3 #Enter side 2: 4 #Enter side 3: 5 t.dispSides() Side 1 is 3.0 Side 2 is 4.0 Side 3 is 5.0 t.findArea() The area of triangle is 6.0
จากตัวอย่าง จะเห็นว่า แม้ว่าเราไม่ได้ทำการกำหนดเมทธอด inputSides() และ dispSides() สำหรับคลาส Triangle แต่เราก็สามารถเรียกใช้งานได้ จากการสืบทอดมาจากคลาส Polygon
ถ้าแอทริบิวท์ที่เรียกใช้งานไม่พบอยู่ในคลาสที่ระบุ จึงทำการค้นหาต่อไปยังคลาสต้นแบบ(base class) และหากคลาสดังกล่าว สืบทอดมาจากคลาสอื่นอีกทีหนึ่ง ก็จะทำเช่นนี้ไปเรื่อยๆ
การ Overriding เมทธอด
จากตัวอย่างที่แล้ว สังเกตุได้ว่า เมทธอด __init__() ถูกกำหนดรายละเอียดในคลาสทั้งในคลาส Polygon และ Triangle เมื่อเกิดกรณีเช่นนี้เมทธอดที่ระบุในคลาสสืบทอด(derived class) จะระบุรายละเอียดให้ใหม่(override) จากรายละเอียดจากคลาสต้นแบบ ซึ่งก็คือคลาส __init__ ใน Triangle ได้ทำการระบุรายละเอียดของฟังก์ชันนี้ใหม่แทนการใช้งานจากฟังก์ชัน __init__ จากคลาส Polygon
โดยส่วนใหญ่เราจะใช้การ overriding นี้เพื่อจะเพิ่มความสามารถให้เมทธอดมากกว่าที่จะเขียนความสามารถแทนที่ลงไปหมด การทำแบบนี้จะทำได้โดยการเรียกใช้งานฟังก์ชันจากคลาสต้นแบบจากฟังก์ชันชื่อเดียวกันภายในคลาสสืบทอด(การเรียก Polygon.__init__() จาก Triangle.__init__())
อีกทางเลือกที่ดีกว่าคือการใช้ built-in ฟังก์ชัน super().__init__(3) ซึ่งก็เทียบเท่ากับเราเรียกใช้ Polygon(self, 3) ซึ่งการเรียกใช้งานแบบนี้ค่อนข้างจะเป็นที่นิยมมากว่า
ยังมี built-in ฟังก์ชัน isinstance() และ issubclass() ซึ่งถูกใช้เพื่อจะตรวจสอบการสืบทอดนั้นๆ โดยฟังก์ชัน isinstance() จะคืนค่าเป็นจริงก็ต่อเมื่อออบเจคนั้นเป็น instance หนึ่งของคลาสนั้น หรือคลาสอื่นที่สืบทอดมาจากคลาสนั้นอีกที โดยทุกคลาสในไพธอนจะสืบทอดมาจากคลาสต้นแบบ object เสมอ
t = Triangle() isinstance(t, Triangle) # True isinstance(t, Polygon) # True isinstance(t, int) # False isinstance(t, object) # True
ในทำนองเดียวกัน ฟังก์ชัน issubclass() ใช้สำหรับตรวจสอบการสืบทอดของคลาสเช่นกัน
t = Triangle() issubclass(Polygon, Triangle) # False issubclass(Triangle, Polygon) # True issubclass(bool, int) # True