การเขียนโปรแกรมเชิงวัตถุ ในภาษา Python
ในบทนี้ คุณจะได้เรียนรู้เกี่ยวกับการเขียนโปรแกรมเชิงวัตถุ (Object-oriented programming) ในภาษา Python ซึ่งเป็นรูปแบบการเขียนโปรแกรมที่มีแนวคิดโดยมองทุกอย่างเป็นวัตถุซึ่งนี่แตกต่างไปจากการเขียนโปรแกรมแบบตามลำดับ และแบบฟังก์ชันนอลที่เราคุ้นเคยกัน นี่เป็นเนื้อหาในบทนี้
- การเขียนโปรแกรมเชิงวัตถุคืออะไร
- คุณสมบัติของการเขียนโปรแกรมเชิงวัตถุ
- คลาสและออบเจ็ค
- Inheritance
- Encapsulation
- Polymorphism
การเขียนโปรแกรมเชิงวัตถุคืออะไร
การเขียนโปรแกรมเชิงวัตถุ (Object-oriented programming หรือ OOP) เป็นรูปแบบการเขียนโปรแกรมที่มองทุกอย่างเป็นวัตถุที่มีทั้งข้อมูลและฟังก์ชันการทำงานในข้อมูลก้อนเดียวนั่นคือออบเจ็ค ซึ่งนี่เป็นคุณสมบัติพื้นฐานของออบเจ็คที่มันสามารถมีทั้งข้อมูลและฟังก์ชันภายในตัวมันสำหรับการทำงานที่สมบูรณ์ นอกจากนี้ การเขียนโปรแกรมเชิงวัตถุยังมีคุณสมบัติที่สามารถนำไปใช้เพื่อช่วยให้การเขียนโปรแกรมมีประสิทธิภาพมากขึ้น เช่น
- Inheritance
- Encapsulation
- Polymorphism
ในบทนี้เราจะพูดถึงแนวคิดพื้นฐานและการใช้งานคุณสมบัติต่างๆ ของการเขียนโปรแกรมเชิงวัตุในภาษา Python หลังจากจบเนื้อหาแล้ว เราหวังว่าคุณจะเข้าใจการเขียนโปรแกรมเชิงวัตถุและสามารถนำไปประยุกต์ใช้ในการเขียนโปรแกรมของคุณได้ มาเริ่มจากเรื่องพื้นฐานกันก่อนนั่นคือคลาสและออบเจ็ค
คลาสและออบเจ็ค
คลาสและออบเจ็ค (Classes and Objects) เป็นสิ่งพื้นฐานในการเขียนโปรแกรมเชิงวัตถุ ออบเจ็คเป็นเหมือนวัตถุที่ประกอบไปด้วยข้อมูลและฟังก์ชันการทำงาน และมันถูกสร้างมาจากคลาส คลาสนั้นทำหน้าที่เป็นเหมือนแม่แบบที่ใช้กำหนดข้อมูลและการทำงานที่ออบเจ็คจะมี นั่นทำให้คลาสสามารถนำไปสร้างเป็นออบเจ็คเป็นจำนวนมากได้ และแต่ละออบเจ็คจะมีสถานะและการทำงานเป็นของมันเอง
ลองจินตนาการว่าคลาสเป็นเหมือนแม่แบบที่ใช้สร้างรถยนต์ที่กำหนดว่ารถยนต์ประกอบไปด้วยรายละเอียดหรือข้อมูลอะไรบ้างและมีการทำงานอะไรบ้าง เมื่อแม่แบบถูกออกแบบเรียบร้อยแล้ว มันถูกส่งต่อให้โรงงานเพื่อนำไปผลิตตามที่ได้ออกเอาแบบไว้ซึ่งก็คือออบเจ็ค จะเห็นว่าแม่แบบถูกเขียนขึ้นเพียงคลาสเดียวแต่สามารถนำไปสร้างรถยนต์เป็นจำนวนมากได้
นี่เป็นตัวอย่างของการประกาศคลาสเพื่อนำไปสร้างออบเจ็คในภาษา Python มาเริ่มต้นจากออบเจ็คของกล่องสี่เหลี่ยมแบบง่ายๆ กัน
class Box:
def __init__(self, width, height):
self.width = width
self.height = height
def getArea(self):
return self.width * self.height
box1 = Box(3, 4)
box2 = Box(5, 3)
print("Area of box1 is %d" % box1.getArea())
print("Area of box2 is %d" % box2.getArea())
นี่เป็นผลลัพธ์การทำงานของโปรแกรม
Area of box1 is 12
Area of box2 is 15
ในตัวอย่างนี้ เราได้สร้างคลาส Box
ที่เป็นเหมือนแม่แบบที่จะนำไปสร้างกล่องหรือออบเจ็ค คลาสนี้ประกอบไปด้วยสองแอททริบิวต์คือ width
และ height
สำหรับเก็บความยาวและความสูงของกล่องตามลำดับ และมีหนึ่งเมธอด getArea
สำหรับคำนวณหาพื้นที่ของกล่องจากความยาวและความสูงของมัน
box1 = Box(3, 4)
box2 = Box(5, 3)
เมื่อคลาสถูกสร้ามันสามารถนำไปสร้างเป็นออบเจ็คได้ ในตัวอย่างนี้ เราได้สร้างสองออบเจ็คของกล่องจากคลาส Box
พร้อมกับกำหนดความยาวและความสูงในตอนที่สร้าง โดยค่านี้จะถูกส่งไปยังคอนสตรัคเตอร์เมธอด __init__
เพื่อกำหนดค่าให้กับแต่ละออบเจ็ค
print("Area of box1 is %d" % box1.getArea())
print("Area of box2 is %d" % box2.getArea())
ในการคำนวณหาพื้นที่ของกล่อง เราสามารถเรียกใช้งานเมธอด getArea
บนออบเจ็คได้ โดยการทำงานของเมธอดจะใช้ข้อมูลจากออบเจ็คที่มันถูกเรียก ตอนนี้คุณจะเห็นแล้วว่าแต่ละออบเจ็คจะมีข้อมูลและเมธอดการทำงานเป็นของมันเองโดยการใช้งานคลาสเดียวกัน
เมื่อคุณได้เห็นตัวอย่างการเขียนโปรแกรมเชิงวัตถุในภาษา Python ไปเบื้องต้นแล้ว ต่อไปเราลองกลับมาเขียนโปรแกรมนี้ในแบบที่ไม่ใช่การเขียนโปรแกรมเชิงวัตถุ เราทำเช่นนี้เพื่อให้คุณเข้าใจความแตกต่างมากขึ้น นี่เป็นตัวอย่างของโปรแกรม
def calBoxArea(width, height):
return width * height
box1_width = 3
box1_height = 4
box2_width = 5
box2_height = 3
print("Area of box1 is %d" % calBoxArea(box1_width, box1_height))
print("Area of box2 is %d" % calBoxArea(box2_width, box2_height))
นี่เป็นผลลัพธ์การทำงานของโปรแกรม โดยเป็นผลลัพธ์เช่นเดียวกับตัวอย่างก่อนหน้า
Area of box1 is 12
Area of box2 is 15
นี่เป็นการเขียนโปรแกรมแสดงการทำงานของกล่องเช่นเดิม แต่ไม่ได้ใช้แนวคิดของการเขียนโปรแกรมเชิงวัตถุแล้ว ดังนั้นข้อมูลของกล่องไม่ว่าจะเป็น ความกว้าง ความยาว และฟังก์ชันคำนวณหาพื้นที่จะถูกประกาศแยกออกจากกัน จะเห็นว่าการเขียนโปรแกรมในรูปแบบนี้มีความเรียบง่าย แต่เมื่อระบบใหญ่และซับซ้อนขึ้นมันก็ทำให้การจัดการยากขึ้นเช่นเดียวกัน ดังนั้นการเขียนโปรแกรมเชิงวัตถุจะช่วยแก้ปัญหานี้ได้โดยรวมโค้ดที่เกี่ยวข้องกันให้เป็นหนึ่งเดียว
Inheritance
การสืบทอดคลาส (Inheritance) คือการที่เราสามารถสร้างคลาสใหม่มาจากคลาสเดิมได้ และเพิ่มเติมความสามารถบางอย่างเข้าไป นี่เป็นการนำโค้ดเดิมกลับมาใช้ใหม่และลดการเขียนโค้ดเดิมซ้ำๆ ยกตัวอย่างเช่น หากเรามีคลาสของทรงกลมอยู่แล้ว เราสามารถสร้างคลาสของลูกบอลโดยสืบทอดมาจากคลาสทรงกลมได้ นั่นเป็นเพราะว่าลูกบอลก็เป็นทรงกลมอยู่แล้ว ดังนั้นสิ่งที่ต้องเพิ่มก็คือคุณสมบัติบางอย่างที่ลูกบอลไม่มี
ต่อไปเราจะสร้างคลาสของกล่องที่มีสีสันโดยสืบทอดมาจากคลาส Box
ในตัวอย่างก่อนหน้า นี่เป็นตัวอย่างของโปรแกรม
class Box:
def __init__(self, width, height):
self.width = width
self.height = height
def getArea(self):
return self.width * self.height
class ColorfulBox(Box):
def __init__(self, width, height, color):
super().__init__(width, height)
self.color = color
def draw(self):
print("Drawing the %s box" % self.color)
for i in range(1, self.height + 1):
print("%s" % ("#" * self.width))
# Creating colorful box
box = ColorfulBox(4, 3, "Red")
print("Box has width %d and height %d" % (box.width, box.height))
print("It's area is %d" % box.getArea())
box.draw()
นี่เป็นผลลัพธ์การทำงานของโปรแกรม
Box has width 4 and height 3
It's area is 12
Drawing the Red box
####
####
####
ในตัวอย่างนี้ เราได้สร้างคลาส ColorfulBox
ที่สืบทอดมาจากคลาส Box
นี่จะทำให้คลาสนี้มีแอททริบิวต์และเมธอดเหมือนกับที่คลาสหลักของมันมี และสิ่งที่เรากำหนดเพิ่มให้กับคลาสนี้คือเพิ่มแอททริบิวต์ color
สำหรับเก็บสีของกล่อง และเมธอด draw
สำหรับวาดกล่องออกทางหน้าจอ
box = ColorfulBox(4, 3, "Red")
print("Box has width %d and height %d" % (box.width, box.height))
print("It's area is %d" % box.getArea())
box.draw()
จากนั้นนำคลาส ColorfulBox
ไปสร้างเป็นออบเจ็คและในตอนนี้จะระบุสีของกล่องด้วย และแสดงข้อมูลเกี่ยวกับออบเจ็คออกทางหน้าจอ เช่น ความยาว ความสูง พื้นที่ และการวาดรูป จะเห็นว่าออบเจ็คที่สร้างมาจากคลาสนี้จะได้รับแอททริบิวต์และเมธอดเหมือนกับที่คลาสหลักของมันมีจากคุณสมบัติการสืบทอดคลาส
และนอกจากนี้ คลาสที่มีอยู่จะสามารถนำไปสืบทอดโดยกี่คลาสหรือจะสืบทอดเป็นลำดับชั้นก็ได้ เช่น เราอาจสร้างคลาสใหม่ที่สืบทอดมาจากคลาส ColorfulBox
เพื่อเพิ่มเติมความสามารถที่มากขึ้นให้กับมัน สำหรับตัวอย่างการใช้งานเพิ่มเติมเกี่ยวกับการสืบทอดคลาส คุณจะได้เรียนรู้อีกครั้งในบท Inheritance ในภาษา Python
Encapsulation
Encapsulation คือคุณสมบัติการห่อหุ้มที่ใช้สำหรับปกปิดข้อมูลในออบเจ็คจากภายนอก นี่จะช่วยรักษาข้อมูลให้มีความปลอดภัยจากการใช้งานมากขึ้น และเมื่อข้อมูลในออบเจ็คถูกปกปิดการเข้าถึงจากภายนอกจะทำให้มันสามารถเข้าถึงได้จากเมธอดภายในคลาสเดียวกันเท่านั้น ดังนั้นการดำเนินการกับข้อมูลจะทำผ่านเมธอดที่เป็น Public แทน
Encapsulation เป็นสามารถพบได้ทั่วไปในชีวิตประจำวัน เช่น เครื่องใช้ไฟฟ้าอย่างทีวีจะเก็บข้อมูลเป็นจำนวนมากในตัวมัน เช่น ระดับเสียงของทีวีเก็บระดับความดังที่ทีวีจะเล่นเสียง และข้อมูลนี่เป็นข้อมูลที่ถูกปกปิดโดยทีวี เนื่องจากเราไม่สามารถเข้าถึงหรืออ่านค่าของมันโดยตรงได้ มาดูตัวอย่างเมื่อเรานำมันมาเขียนในโปรแกรม นี่เป็นคลาสของทีวีที่ว่า
class Television:
__volume = 20
def __init__(self, model):
self.model = model
def volumeUp(self):
self.__volume = self.__volume + 1
if self.__volume > 30:
self.__volume = 30
print("Increased volume up to %d" % self.__volume)
def volumeDown(self):
self.__volume = self.__volume - 1
if self.__volume < 0:
self.__volume = 0
print("Decreased volume down to %d" % self.__volume)
def getVolume(self):
return self.__volume
def info(self):
print("TV Model: %s" % self.model)
print("Current volume: %s%%" % self.__volume)
tv = Television("LG")
# using our TV
print("%s TV has volume level at %d%%." % (tv.model, tv.getVolume()))
tv.volumeUp()
tv.volumeUp()
tv.info()
นี่เป็นผลลัพธ์การทำงานของโปรแกรม
LG TV has volume level at 20%.
Increased volume up to 21
Increased volume up to 22
TV Model: LG
Current volume: 22%
ในตัวอย่างนี้ เราได้ประกาศคลาสของทีวีอย่างง่ายที่ประกอบไปด้วยสองแอททริบิวต์สำหรับเก็บข้อมูลเกี่ยวกับทีวีคือ model
สำหรับเก็บชื่อรุ่นของทีวี และ __volume
สำหรับเก็บระดับเสียงปัจจุบันที่ทีวีกำลังส่งเสียงออกมาอยู่ และมีเมธอดต่างๆ สำหรับควบคุมการทำงานของทีวี
__volume = 20
ในภาษา Python หากแอททริบิวต์ถูกประกาศด้วยเครื่องหมาย Underscore (__
) สองตัวที่หน้าชื่อของตัวแปรจะทำให้ระดับการเข้าถึงของมันเป็นแบบ Private นั่นหมายความว่ามันจะไม่สามารถเข้าถึงได้จากภายนอกออบเจ็คและใช้ได้โดยเมธอดจากคลาสเดียวกันเท่านั้น
def volumeUp(self):
self.__volume = self.__volume + 1
if self.__volume > 30:
self.__volume = 30
print("Increased volume up to %d" % self.__volume)
def volumeDown(self):
self.__volume = self.__volume - 1
if self.__volume < 0:
self.__volume = 0
print("Decreased volume down to %d" % self.__volume)
จากนั้นเราประกาศสองเมธอด volumeUp
และ volumeDown
สำหรับเพิ่มและลดระดับเสียงของทีวีตามลำดับ ในการเพิ่มระดับเสียงจะเห็นว่าระดับสูงสุดจะจำกัดเพียง 30
เท่านั้น และการลดเสียงจะไม่สามารถลดต่ำไปกว่า 0
ได้ และนี่เป็นการปกป้องข้อมูลให้มีความปลอดภัย
จะเห็นว่าแอททริบิวต์ __volume
ถูกปกป้องการเข้าถึงจากภายนอกของออบเจ็ค และการใช้งานมีความปลอดภัย และสามารถทำได้ตามขอบเขตที่กำหนดเท่านั้น นั่นคือคุณสามารถเพิ่มหรือลดเสียงได้ทีละ 1 และต้องอยู่ระหว่างช่วงที่กำหนดเท่านั้น คุณไม่สามารถเพิ่มเสียงทีละ 5 หรือกำหนดเป็น 200% ได้ ยกตัวอย่างเช่น
# Error because this attribute is protected
tv.__volume = 200
การทำเช่นนี้จะทำให้เกิดข้อผิดพลาด เนื่องจากมันเป็นการเข้าถึงแอททริบิวต์จากภายนอกออบเจ็คซึ่งทำไม่ได้สำหรับแอททริบิวต์ที่เป็น Private นี่จะตรงกันข้ามกับแอททริบิวต์ model
ที่มีการเข้าถึงเป็นแบบ Public ดังนั้นเราสามารถเข้าถึงมันโดยตรงเพื่ออ่านและกำหนดค่าได้ ยกตัวอย่างเช่น
print("%s TV has volume level at %d%%." % (tv.model, tv.getVolume()))
ในการแสดงข้อมูลเกี่ยวกับทีวี รุ่นของมันสามารถเข้าถึงได้จากตัวแปรออบเจ็คโดยตรง ในขณะที่ระดับเสียงนั้นจะทำไม่ได้และจะทำผ่านเมธอด getVolume
แทน และนี่เป็นวิธีการทำงานของระดับการเข้าถึงของสมาชิกของออบเจ็คในภาษา Python
def getVolume(self):
return self.__volume
def info(self):
print("TV Model: %s" % self.model)
print("Current volume: %s%%" % self.__volume)
ถัดมาอีกสองเมธอดเป็นเมธอดเกี่ยวกับรับและแสดงข้อมูลเกี่ยวกับทีวี เมธอด getVolume
ใช้สำหรับรับระดับเสียงปัจจุบันของทีวี และเมธอด info
ใช้สำหรับแสดงข้อมูลรุ่นและระดับเสียงของทีวีออกทางหน้าจอ
นอกจากนี้ เมธอดก็สามารถมีระดับการเข้าถึงเป็นแบบ Private ได้เช่นเดียวกัน โดยคุณเพียงตั้งชื่อเมธอดด้วยเครื่องหมาย __
ที่หน้าของมัน นี่จะทำให้มันสามารถเข้าถึงได้จากเมธอดภายในคลาสเดียวกันเท่านั้น โดยปกติแล้วเมธอดที่สร้างในคลาสจะมีระดับการเข้าถึงเป็นแบบ Public เสมอ
Polymorphism
ในการเขียนโปรแกรมเชิงวัตถุ Polymorphism คือคุณสมบัติใช้ที่ใช้กำหนดหรืออ้างถึงออบเจ็คที่แตกต่างกันจากประเภทข้อมูลหรืออินเตอร์เฟซเดียวกัน เนื่องจาก Python เป็นภาษาที่ไม่มี Interface ดังนั้นประเภทข้อมูลที่ใช้จึงมักเป็นคลาสแทน แนวคิดนี้ถูกยืมมาจากหลักการทางชีววิทยาที่สิ่งมีชีวิตหรือสปีชีส์สามารถมีได้หลายรูปแบบหรือหลายสถานะ
กำหนดให้เราต้องการสร้างฟังก์ชันที่ทำให้สัตว์พูดออกมา ในกรณีนี้จะเห็นว่ามันไม่สำคัญว่าสัตว์เหล่านั้นจะเป็นอะไร สิ่งเดียวที่เราต้องการคือมันจะต้องพูดได้ และมันสามารถแสดงได้ในการเขียนโปรแกรมดังนี้
from abc import ABC, abstractmethod
class TalkableAnimal(ABC):
@abstractmethod
def talk(self):
pass
class Cat(TalkableAnimal):
def talk(self):
return "Meow!";
class Dog(TalkableAnimal):
def talk(self):
return "Woof!";
def lets_hear(animal):
print(animal.talk())
lets_hear(Cat())
lets_hear(Dog())
นี่เป็นผลลัพธ์การทำงานของโปรแกรม
Meow!
Woof!
ในตัวอย่างนี้ เป็นการแสดงการมีได้หลายรูปแบบของออบเจ็คโดยใช้แนวคิด Polymorphism ในการเขียนโปรแกรม ในการที่จะระบุว่าสัตว์เหล่านี่เป็นประเภทเดียวกัน เราจะต้องสร้าง Abstract คลาสสำหรับอธิบายรูปแบบที่เหมือนกันของมันก่อน จากนั้นสำหรับสัตว์แต่ละชนิดจะสร้างคลาสอื่นโดยสืบทอดไปจากคลาสนี้แทน
class TalkableAnimal(ABC):
@abstractmethod
def talk(self):
pass
เราสร้าง Abstract คลาสที่มีชื่อว่า TalkableAnimal
ที่หมายถึงคลาสของสัตว์ที่พูดได้ โดย Abstract คลาสเป็นคลาสที่มีอย่างน้อยหนึ่งเมธอดที่เป็น Abstract ซึ่ง Abstract เมธอดจะระบุเพียงรูปแบบของเมธอดเท่านั้นและปราศจากการทำงาน ดังนั้นภายในเมธอดจะเขียนคำสั่ง pass
เพื่อบอกว่ามันยังไม่มีการทำงานในตอนนี้
ในภาษา Python เราสามารถสร้าง Abstract คลาสโดยสืบทอดมันมาจากคลาส ABC
ที่เป็นคลาสมาตรฐานของภาษา และ Abstract เมธอดจะใช้คำสั่ง @abstractmethod
ก่อนการประกาศเมธอด และที่สำคัญ Abstract คลาสจะไม่สามารถนำไปสร้างเป็นออบเจ็คได้โดยมันจะต้องนำไปสืบทอดโดยคลาสอื่นเท่านั้น แต่สามารถใช้เป็นประเภทข้อมูลของออบเจ็คได้
class Cat(TalkableAnimal):
def talk(self):
return "Meow!";
class Dog(TalkableAnimal):
def talk(self):
return "Woof!";
จากนั้นเราได้สร้างสองคลาสของสัตว์ที่สืบทอดมาจากคลาส TalkableAnimal
เมื่อคลาสถูกสืบทอดมาจาก Abstract คลาส มันจะต้องมีการกำหนดการทำงานให้กับ Abstract เมธอดเหมือนกับที่ใน Abstract คลาสมี นั่นคือคลาสทั้งคลาส Cat
และ Dog
จะต้องมีเมธอด talk
ซึ่งในตอนนี้จะมีการทำงานของเป็นของมันเอง ดังนั้นจะถือว่ามันเป็นคลาสของสัตว์พูดได้แล้วจากการสืบทอดมาจากคลาส TalkableAnimal
และกำหนดการทำงานให้กับเมธอด
def lets_hear(animal: TalkableAnimal):
print(animal.talk())
สุดท้ายเราได้สร้างฟังก์ชัน lets_hear
ที่รับพารามิเตอร์ animal
สำหรับจำลองการทำงานการพูดของสัตว์ และเราเรียกเมธอด talk
เพื่อทำให้สัตว์ส่งเสียงร้อง จะเห็นว่าออบเจ็คที่ส่งมายังฟังก์ชันจะหมายออบเจ็คของสัตว์ที่พูดไม่ว่าจะเป็นแมวหรือสุนัข ซึ่งนี่แสดงถึงการมีได้หลายรูปแบบของสัตว์ และนี่เป็นวิธีการประยุดต์ใช้ Polymorphism ในการเขียนโปรแกรม
Notes: ในภาษา Python ตั้งแต่เวอร์ชัน 3.5 ขึ้นไป คุณสามารถกำหนดประเภทข้อมูลให้กับตัวแปรหรือพารามิเตอร์ของฟังก์ชันได้ ในตัวอย่าง เราได้กำหนดประเภทข้อมูลให้กับพารามิเตอร์
animal
เป็นTalkableAnimal
นี่จะทำให้ออบเจ็คที่ส่งเข้ามายังฟังก์ชันต้องเป็นออบเจ็คที่สืบทอดมาจากคลาสนี้เท่านั้น
ในบทนี้ คุณได้เรียนรู้และทำความรู้จักกับการเขียนโปรแกรมเชิงวัตถุในภาษา Python เราได้พูดถึงว่าการเขียนโปรแกรมเชิงวัตถุคืออะไร การใช้งานคลาสและออบเจ็ค และพูดถึงคุณสมบัติที่สำคัญของการเขียนโปรแกรมเชิงวัตถุ เช่น การสืบทอด การห่อหุ้ม และการมีได้หลายรูปแบบ ที่สามารถนำไปประยกต์ใช้เพื่อให้การเขียนโปรแกรมมีประสิทธิภาพขึ้นได้