Python – Objects and Class

ไพธอนเป็นหนึ่งในภาษาที่เป็น Object Oriented ซึ่งต่างจาก procedure oriented language ที่จะเน้นการใช้งานเป็นฟังก์ชัน แต่ Object Oriented จะเน้นการใช้งานในรูปแบบออบเจค

โดยออบเจ็คแต่ละชนิดนั้นประกอบด้วยข้อมูลตัวแปรและฟังก์ชันเพื่อมาใช้ทำงานกับข้อมูลต่างๆ และคลาสซึ่งเป็นการกำหนดรูปแบบให้ออบเจ็คแต่ละชนิด

เราสามารถเปรียบเทียบคลาสเป็นเหมือนโครงร่างของบ้าน ซึ่งจะประกอบด้วยข้อมูลของพื้น, ประตู, หน้าต่าง และอื่นๆ ขึ้นอยู่กับรายละเอียดนี้เราสามารถประกอบเป็นบ้าน โดยบ้านเป็นออบเจ็คหนึ่ง ซึ่งบ้านหลายๆหลังสามารถสร้างจากการรายละเอียดเริ่มต้นเหล่านี้ ซึ่งสามารถสร้างเป็นออบเจ็คได้หลายแบบจากคลาสเดียวกันซึ่งจะเรียกว่า instance ของคลาส

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

class abc
     ''' docstring here '''
     pas

คลาสจะทำการสร้าง local namespace ขึ้นมาใหม่ซึ่งใช้ประกาศแอทริบิวต์หรือฟังก์ชัน และยังมีแอททริบิวท์พิเศษที่เริ่มต้นชื่อด้วย __ เช่น __doc__ ใช้กำหนดรายละเอียด docstring ของคลาส

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

class MyClass:
	"This is my second class"
	a = 10
	def func(self):
		print('Hello')

# Output: 10
print(MyClass.a)

# Output: 
print(MyClass.func)

# Output: 'This is my second class'
print(MyClass.__doc__)

การสร้างออบเจ็ค
นอกจากเราสามารถใช้ออบเจ็คของคลาสเพื่อเข้าถึงข้อมูลแอทริบิวท์ต่างๆ เรายังสามารถใช้เพื่อสร้างออบเจ็ค instance ของคลาสนั้นๆ ได้ด้วย วิธีการสร้างออบเจ็คเหมือนกับการเรียกใช้ฟังก์ชัน
ตัวอย่างการใช้งาน

newobj = NameObj()

จากตัวอย่างจะทำการสร้างออบเจ็ค instance ชื่อว่า ob เราสามารถเข้าถึงข้อมูลแอทริบิวท์ของออบเจ็คโดยใช้ชื่อออบเจ็คในการขึ้นต้น

โดยคุณลักษณะที่จะเข้าถึงอาจจะเป็นข้อมูล หรือเมทธอด โดยเมทธอดของออบเจ็คนั้นสัมพันธ์กับฟังก์ชันของคลาส ฟังก์ชันของออบเจ็คใดคือแอทริบิวท์ของคลาส สำหรับกำหนดเมทธอดของออบเจ็คของคลาสนั้น
เช่น MyClass.func เป็นฟังก์ชันของออบเจค(แอทริบิวท์ของคลาส) obj.func จะเป็นเมทธอดของออบเจ็ค

class MyClass:
	"This is my second class"
	a = 10
	def func(self):
		print('Hello')

# create a new MyClass
ob = MyClass()

# Output: 
print(MyClass.func)

# Output: >
print(ob.func)

# Calling function func()
# Output: Hello
ob.func()

เราอาจจะพบตัวแปร self ในการกำหนดฟังก์ชันของคลาส แต่ว่าเวลาเราต้องการเรียกใช้เมทธอดเราเรียกใช้โดยไม่ทำการระบุค่านี้เข้าไปเช่น obj.func() เนื่องจากทุกครั้งที่ออบเจคทำการเรียกใช้เมทธอด ตัวออบเจคเองจะถูกส่งเข้าไปเป็นตัวแปรแรกเสมอ ดังนั้นเมื่อเราเรียก obj.func() สิ่งที่เกิดขึ้นคือจะถูกแปลงเป็น NewClass.func(obj) ซึ่งโดยทั่วไปเมื่อมีการเรียกใช้งานเมทธอดพร้อมกับลิสต์ของตัวแปร n ตัว จะเทียบเท่ากับการเรียกใช้งานฟังก์ชันพร้อมทั้งลิสต์ของตัวแปรนั้น ซึ่งถูกสร้างขึ้นโดยทำการเพิ่ม ออบเจคของเมทธอดเข้าไปเป็นตัวแรกของกลุ่มตัวแปร

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

constructors
คลาสฟังก์ชันที่เริ่มต้นชื่อด้วย __ นั้นเรียกว่าฟังก์ชันพิเศษ หนึ่งในฟังก์ชันลักษณะดังกล่าว คือ ฟังก์ชัน __init__() โดยฟังก์ชันนี้จะถูกเรียกใช้เสมอเมื่อมีการสร้าง instance ของออบเจคใหม่ ซึ่งฟังก์ชันนี้จะเรียกว่า constructor สำหรับ OOP ซึ่งมักจะถูกนำไปใช้สำหรับทำการกำหนดค่าตั้งตั้งให้กับคลาสนั้น
ตัวอย่างการใช้งาน

class ComplexNumber:
    def __init__(self,r = 0,i = 0):
        self.real = r
        self.imag = i

    def getData(self):
        print("{0}+{1}j".format(self.real,self.imag))

# Create a new ComplexNumber object
c1 = ComplexNumber(2,3)

# Call getData() function
# Output: 2+3j
c1.getData()

# Create another ComplexNumber object
# and create a new attribute 'attr'
c2 = ComplexNumber(5)
c2.attr = 10

# Output: (5, 0, 10)
print((c2.real, c2.imag, c2.attr))

# but c1 object doesn't have attribute 'attr'
# AttributeError: 'ComplexNumber' object has no attribute 'attr'
c1.attr

จากตัวอย่าง เรากำหนดคลาสใหม่ซ฿่งแสดงข้อมูลจำนวนเชิงซ้อน โดยกำหนดให้คลาสมี 2 ฟังก์ชันคือ __init__() เพื่อจะกำหนดค่าตั้งต้นให้ตัวแปร และ getData() เพื่อจะแสดงจำนวนด้วยรูปแบบที่เหมาะสม
ประเด็นที่น่าสนใจคือ แอทริบิวท์ของออบเจคสามารถจะถูกสร้างตอนโปรแกรมทำงานแล้วได้ จากตัวอย่างเราทำการสร้างแอทริบิวท์ใหม่ชื่อ attr ตอนโปรแกรมถูกใช้งานไปแล้วสำหรับออบเจค c2 และอ่านค่าออกมาด้วย แต่ไม่ได้สร้างทำการสร้าง attribute attr สำหรับออบเจค c1 หากมีการเรียกใช้งานอาจทำให้เกิดข้อผิดพลาดได้

การลบข้อมูลแอทริบิวท์ และ ออบเจค
เราสามารถลบข้อมูลแอทริบิวท์หรือออบเจคโดยใช้คีย์เวิร์ด del
ตัวอย่างการใช้งาน

c1 = ComplexNumber(2, 3)
del c1.imag
c1.getData()
Traceback (most recent call last):
...
AttributeError: 'ComplexNumber' object has no attribute 'imag'

del ComplexNumber.getData
c1.getData()
Traceback (most recent call last):
...
AttributeError: 'ComplexNumber' object has no attribute 'getData'

del c1
c1
Traceback (most recent call last):
...
NameError: name 'c1' is not defined

แต่ในระบบด้านหลังนั้นจะซับซ้อนกว่า ตรงที่เมื่อเรากำหนด c1 = ComplexNumber(1, 3) instance ใหม่ของออบเจคได้ถูกสร้างขึ้นในหน่วยความจำและชื่อ c1 ได้ถูก bind ไว้กับหน่วยความจำนั้น เมื่อใช้คำสั่ง del c1 ข้อมูลการ bind ได้ถูกลบออกและชื่อ c1 ได้ถูกลบออกจาก namespace แต่ตัวออบเจคเองนั้นยังคงอยู่ในหน่วยความจำ ถ้าไม่มีชื่อใดมาทำการ bind ไว้ ออบเจคนั้นจึงค่อยถูกลบออกอัตโนมัติ ซึ่งการลบออบเจคที่ไม่มีการอ้างถึงนี้อัตโนมัติดนั้นเรียกว่า garbage collection