คลาสและออบเจ็ค

ในบทนี้ คุณจะได้เรียนรู้เกี่ยวกับคลาส (Classes) และออบเจ็ค (Objects) ในภาษา Python เราจะแนะนำให้คุณรู้จักกับแนวคิดของการเขียนโปรแกรมเชิงวัตถุ (Object oriented programming หรือ OOP) เช่นเดียวกับภาษาอื่นๆ Python ถือเป็นหนึ่งในภาษาที่เป็น OOP โดยพื้นฐาน อย่างไรก็ตาม แนวคิดการเขียนโปรแกรมเชิงวัตถุในภาษา Python นั้นอาจจะแตกต่างจากภาษาอื่นเล็กน้อย เช่น คุณสามารถสืบทอดจากหลายคลาสได้ในเวลาเดียวกัน และมันไม่มีคุณสมบัติการห่อหุ้มข้อมูลและไม่มี Interfaces เป็นต้น

ก่อนที่จะเริ่มต้น มาทำความเข้าใจกับคำศัพท์ที่จำเป็นต้องทราบสำหรับการเขียนโปรแกรมเชิงวัตถุในภาษา Python

  • คลาส คือประเภทข้อมูลที่สร้างโดยผู้ใช้ โดยจะนำไปใช้สร้างออบเจ็ค กล่าวอีกนัยหนึ่ง คลาสคือประเภทข้อมูลของออบเจ็ค
  • ออบเจ็ค คือสิ่งที่สร้างมาจากคลาสหรือ class instances
  • แอตทริบิวต์ (instance attributes) คือข้อมูลที่เป็นสมาชิกของแต่ละออบเจ็ค โดยมักจะกำหนดไว้ในเมธอด __init__() ของคลาส
  • เมธอด คือฟังก์ชันการทำงานที่กำหนดไว้ในคลาส
  • คลาสแอตทริบิวต์ (class attributes) คือตัวแปรที่ประกาศไว้ในคลาส ซึ่งจะแชร์กับออบเจ็คทั้งหมดที่สร้างจากคลาสนั้นๆ

คลาสคืออะไร

คลาส คือสิ่งที่ใช้กำหนดรูปแบบของข้อมูล (Attributes) และเมธอด (Methods) การทำงานเข้าด้วยกัน การสร้างคลาส หมายถึงการสร้างประเภทของออบเจ็คขึ้นมา กล่าวอีกนัยหนึ่ง คลาสคือประเภทข้อมูลของออบเจ็คโดยคลาสนั้นสร้างขึ้นโดยผู้ใช้ (User-defined type) โดยปกติแล้ว ประเภทข้อมูลพื้นฐานในภาษา Python นั้นคือคลาส เมื่อคุณสร้างตัวแปรใดๆ ขึ้นมา ตัวแปรเหล่านั้นเป็นออบเจ็คของคลาส เพื่อทำให้คุณเข้าใจมากขึ้นมาดูตัวอย่างต่อไปนี้

a = 1
b = 1.34
c = 'marcuscode.com'

print(type(a))
print(type(b))
print(type(c))

ในตัวอย่าง เราได้ประกาศตัวแปรสามตัวคือ Integer Floating และ String ตามลำดับ ดังนั้นตัวแปรเหล่านี้ถือว่าเป็นออบเจ็คของคลาส ดังผลลัพธ์ข้างล่าง

<class 'int'>
<class 'float'>
<class 'str'>

เหมือนที่คุณเห็น เราใช้ฟังก์ชัน type() เพื่อดูประเภทข้อมูลของออบเจ็คใดๆ หรือใช้สำหรับดูประเภทของคลาสที่มันสร้างมาจาก จากตัวอย่างนั้น เราเรียกคลาส int float และ str ว่า build-in type สำหรับในบทนี้ เรากำลังจะพูดเกี่ยวกับการสร้างคลาสซึ่งเป็น User-defined type นั่นเอง

การสร้างคลาสและออบเจ็ค

คลาส จะประกอบไปด้วยสมาชิกสองประเภท คือ แอตทริบิวต์หรือตัวแปร ที่ใช้ในการเก็บข้อมูลภายในคลาสนั้นๆ และเมธอด (methods) เป็นฟังก์ชันการทำงานหรือจัดการข้อมูลในคลาสนั้น เมธอดนั้นคล้ายกับฟังก์ชัน แต่ในบริบทของการเขียนโปรแกรมเชิงวัตถุแล้วเราจะเรียกว่าเมธอดแทน เพราะว่ามันถูกประกาศอยู่ภายในคลาส นี่เป็นรูปแบบของการสร้างคลาสในภาษา Python

class ClassName:
# statements

เราจะใช้คำสั่ง class สำหรับสร้างคลาสในภาษา Python และตามด้วยชื่อของคลาส ClassName ชื่อของคลาสควรจะขึ้นต้นด้วยตัวใหญ่และเป็นรูปแบบ camel case หลังจากนั้นเป็นคำสั่งในการกำหนดตัวแปรและเมธอดของคลาส ต่อไปมาดูตัวอย่างการสร้างคลาสในภาษา Python

class Book:

def __init__(self, name, price):
self.name = name
self.price = price

def getDetail(self):
print('Name: %s' % self.name)
print('Price: %d USD' % self.price)

ในตัวอย่าง เราได้สร้างคลาส Book และภายในมีเมธอด __init__() ซึ่งเป็นคอนสตรัคเตอร์ (Constructor) ซึ่งจะถูกเรียกอัตโนมัติเมื่อออบเจ็คถูกสร้างสำเร็จ พารามิเตอร์แรกของเมธอดจะเป็น self เสมอ นี่จะใช้เป็นตัวแปรในการอ้างถึงออบเจ็คปัจจุบัน คลาสนี้จะมีสองแอตทริบิวต์คือ name และ price ใช้สำหรับเก็บชื่อและราคาของหนังสือของแต่ละออบเจ็ค หลังจากที่เราได้สร้างคลาสเสร็จแล้ว ต่อไปเราจะนำมาสร้างออบเจ็ค

b1 = Book('Python language', 59)
b2 = Book('C++ language', 69)

b1.getDetail()
b2.getDetail()

b1.price = 99

b1.getDetail()

ในตัวอย่าง เราได้สร้างตัวแปรออบเจ็คจากคลาส Book สองตัวแปร คือ b1 และ b2 สำหรับพารามิเตอร์แรกในคอนสตรัคเตอร์นั้นเราได้ละเว้นไป เพราะ Python จะใส่เป็นออบเจ็คปัจจุบันให้อัตโนมัติ ดังนั้นพารามิเตอร์ที่เราจะต้องใส่คือชื่อและราคาของหนังสือ

หลังจากที่ออบเจ็คถูกสร้างแล้ว ทั้ง b1 และ b2 จะมีแอตทริบิวต์ name price และเมธอด getDetail() เป็นของตัวเองที่ไม่เกี่ยวข้องกัน ในการเข้าถึงสมาชิกภายในออบเจ็คจะใช้เครื่องหมายจุด (.) ดังนั้นเมื่อเราเรียก b1.getDetail() จะเป็นการแสดงรายละเอียดข้อมูลในออบเจ็ค b1 ซึ่ง b2 ก็เช่นเดียวกัน และต่อมาเราได้เปลี่ยนค่า price ของออบเจ็ค b1 ให้มีค่าเป็น 99 และแสดงรายละเอียดอีกครั้ง และนี่เป็นผลลัพธ์การทำงานของโปรแกรม

Name: Python language
Price: 59 USD
Name: C++ language
Price: 69 USD
Name: Python language
Price: 99 USD

ในตอนนี้ คุณได้เห็นแล้วว่าคลาสหนึ่งคลาสสามารถนำไปสร้างเป็นออบเจ็คกี่อันก็ได้ ซึ่งนี่เองถือว่าเป็นแนวคิดที่สำคัญของการเขียนโปรแกรมเชิงวัตุในการนำโค้ดกลับมาใช้ซ้ำ

หลังจากที่คุณได้เห็นการประกาศคลาสและสร้างออบเจ็คในเบื้องต้นแล้ว ต่อไปมาดูตัวอย่างเพิ่มเติมสำหรับการทำงานกับคลาสในภาษา Python

class Person:

def __init__(self, firstName, lastName):
self.firstName = firstName
self.lastName = lastName

def getName(self):
return self.firstName + ' ' + self.lastName

p = Person('Chase', 'Rice')
p.career = 'Singer'
p.country = 'USA'

print('Name: ' + p.getName())
print('Career: ' + p.career)
print('Country: ' + p.country)

p2 = Person('Max', 'Graham')
p2.genres = ['Electronica', 'trance', 'tech house', 'techno']

print('Name: ' + p2.getName())
print('Genres: ', p2.genres)

ในตัวอย่าง เราได้สร้างคลาส Person ที่ประกอบไปด้วยแอตทริบิวต์ firstName และ lastName และเมธอด getName() สำหรับรับชื่อกับนามสกุลพร้อมกัน และเราได้กำหนดค่าให้กับแอตทริบิวต์ career และ country ในภายหลัง ซึ่งโดยปกติแล้วในการกำหนดแอตทริบิวต์เริ่มต้นควรจะทำในเมธอด __init__() ดังนั้น นี่จะทำให้คุณเห็นว่าในภาษา Python เราสามารถสร้างแอตทริบิวต์ใดๆ ในขณะที่โปรแกรมทำงานได้

Name: Chase Rice
Career: Singer
Country: USA
Name: Max Graham

นี่เป็นผลลัพธ์การทำงานของโปรแกรม คุณได้เห็นแล้วว่าการเขียนโปรแกรมในภาษา Python นั้นค่อนข้างยืดหยุ่น คุณไม่จำเป็นต้องประกาศแอตทริบิวต์ทั้งหมดไว้ในตอนแรกก็ได้ คุณอาจจะเพิ่มเข้ามาในภายหลังเฉพาะออบเจ็คที่ต้องการได้ เหมือนในออบเจ็ค p2 ไม่ได้ต้องการมีแอตทริบิวต์เหมือนกับออบเจ็ค p แต่ทั้งสองยังมีแอตทริบิวต์บางอย่างที่เหมือนกัน

Constructor และ Destructor

ต่อมาเราจะพูดเกี่ยวกับการใช้งาน Constructor และ Destructor ในภาษา Python นั้นมีเมธอดพิเศษ (Special methods) ที่สร้างให้อัตโนมัติเมื่อคุณสร้างคลาสขึ้นมา เพื่อใช้งานเราจะต้อง override เมธอดเหล่านั้น เมธอดแรกคือ __init__() ซึ่งเมธอดนี้จะทำงานเมื่อออบเจ็คถูกสร้างสำเร็จ หรือเรียกว่า Constructor มันมักจะใช้ในการกำหนดแอตทริบิวต์และค่าเริ่มต้นให้กับออบเจ็ค ต่อมาคือเมธอด __del__() ซึ่งเมธอดนี้จะทำงานเมื่อออบเจ็คถูกทำลาย หรือเรียกว่า Destructor มาดูตัวอย่างการใช้งาน

class Person:

def __init__(self, firstName, lastName):
self.firstName = firstName
self.lastName = lastName
print('Object was created')

def getName(self):
return self.firstName + ' ' + self.lastName

def __del__(self):
print('Object was destroyed')

p = Person('Chase', 'Rice')
print(p.getName())
del p

ในตัวอย่าง เราได้ทำการ override เมธอด __init__() มันถูกเรียกอัตโนมัติเมื่อเราสร้างออบเจ็คสำเร็จ จากคำสั่ง Person('Chase', 'Rice') หลังจากนั้นเราได้ทำการ override เมธอด __del__() ซึ่งเมธอดนี้จะถูกเรียกใช้งานก่อนที่ออบเจ็คจะถูกทำลาย ในโค้ดเราได้ทำลายออบเจ็คด้วยคำสั่ง del p ซึ่งจะทำให้ออบเจ็คถูกลบออกไปจากหน่วยความจำ

Object was created
Chase Rice
Object was destroyed

นี่เป็นผลลัพธ์การทำงานของโปรแกรม เมื่อสร้างออบเจ็คสำเร็จเราได้แสดงข้อความในเมธอด __init__() ว่าสร้างออบเจ็คสำเร็จแล้ว และเมื่อเราลบออบเจ็คโปรแกรมแสดงข้อความในเมธอด __del__() ว่าออบเจ็คถูกทำลายไปแล้ว ซึ่งการทำงานเหล่านี้จะเกิดขึ้นอัตโนมัติ แน่นอนว่าในภาษา Python เรามักจะใช้คอนสตรัคเตอร์เสมอ แต่ว่า Destructor มักจะไม่ได้ใช้บ่อยนัก อย่างไรก็ตามยังมีเมธอดพิเศษอื่นๆ อีกที่เรายังไม่ได้พูดถึงในบทนี้

Static variables และ Static methods

สำหรับเรื่องสุดท้ายที่คุณจะได้เรียนในบทนี้ คือการใช้งาน static variable และ static method โดย static variable หรือคลาสแอตทริบิวต์ คือตัวแปรที่ประกาศภายในคลาสซึ่งตัวแปรนี้จะแชร์กับออบเจ็คทุกอันที่สร้างจากคลาสนี้ ส่วน static method เป็นเมธอดที่สร้างไว้ในคลาสแต่ไม่ได้มีส่วนเกี่ยวกับข้องกับการจัดการออบเจ็ค มาดูตัวอย่างการใช้งาน

class Box:

# shared variable for all object create by this class
color = 'green'

# class method for object
def __init__(self, width, height, dept):
self.width = width
self.height = height
self.dept = dept

# class method for object
def getVolume(self):
return self.width * self.height * self.dept

@staticmethod
def compare(a, b):
if a.getVolume() > b.getVolume():
return 'greater than'
elif a.getVolume() == b.getVolume():
return 'equal'
else:
return 'less than'

a = Box(2, 3, 4)
b = Box(1, 2, 5)

Box.color = 'red'

print('Box a volume = %d' % a.getVolume())
print('Box b volume = %d' % b.getVolume())

print('Box a color = %s' % a.color)
print('Box b color = %s' % b.color)

print('Box a volume a is %s box b' % Box.compare(a, b))

ในตัวอย่าง เราได้ประกาศคลาส Box โดยคลาสนี้มีตัวแปรของคลาส คือ color นั่นหมายความว่าตัวแปรนี้จะถูกใช้งานร่วมกันกับทุกออบเจ็คที่สร้างจากคลาสนี้ ต่อมาภายในเมธอด __init__() เป็นการกำหนดข้อมูลให้กับออบเจ็คแต่ละอันที่จะมีความกว้าง ความยาว และความสูง และเมธอด getVolume() สำหรับรับปริมาตรของกล่อง

ภายในคลาส Box เราได้สร้าง static method compare() โดยใช้ decorator @staticmethod นำหน้าก่อนหนึ่งบรรทัด เมธอดนี้ไม่ได้มีส่วนเกี่ยวข้องกับข้อมูลภายในคลาส สังเกตุว่ามันไม่มีพารามิเตอร์ self ถูกส่งเข้ามา สำหรับการเรียกใช้งาน static variables หรือ static method นั้นจะไม่ขึ้นกับออบเจ็ค นั่นหมายความว่าเราจะใช้ชื่อของคลาสแทน ในคำสั่ง Box.color = 'red' เป็นการกำหนดสีให้กับกล่องทุกกล่องเป็นสีแดง และคำสั่ง Box.compare(a, b) เป็นการเปรียบเทียบปริมาตรของกล่องทางซ้ายว่ามากกว่าทางขวาหรือไม่

Box a volume = 24
Box b volume = 10
Box a color = red
Box b color = red
Box a volume a is greater than box b

นี่เป็นผลลัพธ์การทำงานของโปรแกรม จะเห็นว่าเราได้ทำการเปลี่ยนค่าสีของกล่องเป็นสีแดงในคำสั่งเดียว แต่มันถูกนำไปใช้กับทุกกล่อง และเมธอดสำหรับเปรียบเทียบปริมาตรของกล่องมันสามารถใช้ได้โดยไม่ขึ้นกับออบเจ็คใดๆ นั่นหมายความว่า static method อาจจะเป็นเมธอดที่ทำงานเกี่ยวกับออบเจ็คที่สร้างจากคลาสนี้ แต่ไม่ได้เป็นเมธอดสำหรับจักการข้อมูลในคลาสโดยตรง

ในบทนี้ คุณได้เรียนรู้เกี่ยวกับคลาสและออบเจ็คในภาษา Python ซึ่งเป็นสิ่งที่คุณควรจะทำความเข้าใจเพื่อที่จะใช้ประโยชน์จาก OOP ซึ่งมันช่วยให้การเขียนโปรแกรมมีประสิทธิภาพและรวดเร็วขึ้น นอกจากนี้เรายังพูดเกี่ยวกับเมธอดพิเศษ Constructor และ Destructor และสมาชิกที่เป็น static ของคลาส สำหรับเนื้อหาของ OOP ยังไม่จบเพียงเท่านี้ มันยังมีการสืบทอด (Inheritance) ซึ่งคุณจะได้เรียนในบทต่อไป