Python - เชิงวัตถุ
Python เป็นภาษาเชิงวัตถุมาตั้งแต่มีอยู่ ด้วยเหตุนี้การสร้างและใช้คลาสและวัตถุจึงเป็นเรื่องง่าย บทนี้ช่วยให้คุณเป็นผู้เชี่ยวชาญในการใช้การสนับสนุนการเขียนโปรแกรมเชิงวัตถุของ Python
หากคุณไม่เคยมีประสบการณ์เกี่ยวกับการเขียนโปรแกรมเชิงวัตถุ (OO) มาก่อนคุณอาจต้องการปรึกษาหลักสูตรเบื้องต้นเกี่ยวกับเรื่องนี้หรืออย่างน้อยก็มีการสอนเกี่ยวกับเรื่องนี้เพื่อให้คุณเข้าใจแนวคิดพื้นฐาน
อย่างไรก็ตามนี่คือการแนะนำเล็ก ๆ น้อย ๆ ของ Object-Oriented Programming (OOP) เพื่อให้คุณได้รับความเร็ว -
ภาพรวมของคำศัพท์ OOP
Class- ต้นแบบที่ผู้ใช้กำหนดเองสำหรับอ็อบเจ็กต์ที่กำหนดชุดของแอ็ตทริบิวต์ที่แสดงลักษณะของอ็อบเจ็กต์ใด ๆ ของคลาส แอตทริบิวต์คือสมาชิกข้อมูล (ตัวแปรคลาสและตัวแปรอินสแตนซ์) และวิธีการเข้าถึงผ่านสัญกรณ์จุด
Class variable- ตัวแปรที่แชร์โดยอินสแตนซ์ทั้งหมดของคลาส ตัวแปรคลาสถูกกำหนดภายในคลาส แต่อยู่นอกเมธอดของคลาส ตัวแปรคลาสไม่ได้ใช้บ่อยเท่ากับตัวแปรอินสแตนซ์
Data member - ตัวแปรคลาสหรือตัวแปรอินสแตนซ์ที่เก็บข้อมูลที่เกี่ยวข้องกับคลาสและอ็อบเจ็กต์
Function overloading- การกำหนดพฤติกรรมมากกว่าหนึ่งอย่างให้กับฟังก์ชันหนึ่ง ๆ การดำเนินการจะแตกต่างกันไปตามประเภทของวัตถุหรือข้อโต้แย้งที่เกี่ยวข้อง
Instance variable - ตัวแปรที่กำหนดภายในวิธีการและเป็นของอินสแตนซ์ปัจจุบันของคลาสเท่านั้น
Inheritance - การถ่ายโอนคุณสมบัติของคลาสไปยังคลาสอื่น ๆ ที่ได้มาจากคลาส
Instance- วัตถุส่วนบุคคลของคลาสหนึ่ง ๆ วัตถุ obj ที่เป็นของคลาส Circle เช่นอินสแตนซ์ของคลาส Circle
Instantiation - การสร้างอินสแตนซ์ของคลาส
Method - ฟังก์ชันพิเศษที่กำหนดไว้ในนิยามคลาส
Object- อินสแตนซ์เฉพาะของโครงสร้างข้อมูลที่กำหนดโดยคลาส ออบเจ็กต์ประกอบด้วยสมาชิกข้อมูล (ตัวแปรคลาสและตัวแปรอินสแตนซ์) และวิธีการ
Operator overloading - การกำหนดมากกว่าหนึ่งฟังก์ชันให้กับตัวดำเนินการเฉพาะ
การสร้างชั้นเรียน
ระดับคำสั่งสร้างนิยามคลาสใหม่ ชื่อของคลาสจะตามหลังคลาสคีย์เวิร์ดตามด้วยเครื่องหมายจุดคู่ดังนี้ -
class ClassName:
'Optional class documentation string'
class_suite
ชั้นมีสตริงเอกสารซึ่งสามารถเข้าถึงได้ผ่านทางClassName .__ doc__
class_suiteประกอบด้วยทั้งหมดงบองค์ประกอบกำหนดสมาชิกชั้นแอตทริบิวต์ข้อมูลและฟังก์ชั่น
ตัวอย่าง
ต่อไปนี้เป็นตัวอย่างคลาส Python อย่างง่าย -
class Employee:
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print "Total Employee %d" % Employee.empCount
def displayEmployee(self):
print "Name : ", self.name, ", Salary: ", self.salary
ตัวแปรempCountเป็นตัวแปรคลาสที่มีการแชร์ค่าระหว่างอินสแตนซ์ทั้งหมดของคลาสนี้ สามารถเข้าถึงได้ในรูปแบบEmployee.empCountจากในชั้นเรียนหรือนอกชั้นเรียน
วิธีแรก__init __ ()เป็นวิธีพิเศษซึ่งเรียกว่า class constructor หรือ initialization method ที่ Python เรียกเมื่อคุณสร้างอินสแตนซ์ใหม่ของคลาสนี้
คุณประกาศวิธีการเรียนอื่น ๆ เช่นฟังก์ชั่นปกติมีข้อยกเว้นว่าอาร์กิวเมนต์แรกแต่ละวิธีเป็นตัวเอง Python เพิ่มอาร์กิวเมนต์selfในรายการให้คุณ คุณไม่จำเป็นต้องรวมไว้เมื่อคุณเรียกใช้วิธีการ
การสร้างวัตถุอินสแตนซ์
ในการสร้างอินสแตนซ์ของคลาสคุณเรียกใช้คลาสโดยใช้ชื่อคลาสและส่งผ่านอาร์กิวเมนต์ใด ๆ ก็ตามที่เมธอด__init__ยอมรับ
"This would create first object of Employee class"
emp1 = Employee("Zara", 2000)
"This would create second object of Employee class"
emp2 = Employee("Manni", 5000)
การเข้าถึงแอตทริบิวต์
คุณเข้าถึงแอตทริบิวต์ของวัตถุโดยใช้ตัวดำเนินการจุดกับวัตถุ ตัวแปรคลาสจะเข้าถึงได้โดยใช้ชื่อคลาสดังนี้ -
emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount
ตอนนี้รวบรวมแนวคิดทั้งหมดเข้าด้วยกัน -
#!/usr/bin/python
class Employee:
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print "Total Employee %d" % Employee.empCount
def displayEmployee(self):
print "Name : ", self.name, ", Salary: ", self.salary
"This would create first object of Employee class"
emp1 = Employee("Zara", 2000)
"This would create second object of Employee class"
emp2 = Employee("Manni", 5000)
emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount
เมื่อดำเนินการโค้ดด้านบนจะให้ผลลัพธ์ดังนี้ -
Name : Zara ,Salary: 2000
Name : Manni ,Salary: 5000
Total Employee 2
คุณสามารถเพิ่มลบหรือแก้ไขแอตทริบิวต์ของคลาสและวัตถุได้ตลอดเวลา -
emp1.age = 7 # Add an 'age' attribute.
emp1.age = 8 # Modify 'age' attribute.
del emp1.age # Delete 'age' attribute.
แทนที่จะใช้คำสั่งปกติในการเข้าถึงแอตทริบิวต์คุณสามารถใช้ฟังก์ชันต่อไปนี้ -
getattr(obj, name[, default]) - เพื่อเข้าถึงคุณลักษณะของวัตถุ
hasattr(obj,name) - เพื่อตรวจสอบว่ามีแอตทริบิวต์อยู่หรือไม่
setattr(obj,name,value)- เพื่อตั้งค่าแอตทริบิวต์ หากไม่มีแอตทริบิวต์ก็จะถูกสร้างขึ้น
delattr(obj, name) - เพื่อลบแอตทริบิวต์
hasattr(emp1, 'age') # Returns true if 'age' attribute exists
getattr(emp1, 'age') # Returns value of 'age' attribute
setattr(emp1, 'age', 8) # Set attribute 'age' at 8
delattr(empl, 'age') # Delete attribute 'age'
แอตทริบิวต์คลาสในตัว
ทุกคลาส Python จะติดตามแอตทริบิวต์ในตัวและสามารถเข้าถึงได้โดยใช้ dot operator เหมือนกับแอตทริบิวต์อื่น ๆ -
__dict__ - พจนานุกรมที่มีเนมสเปซของชั้นเรียน
__doc__ - สตริงเอกสารประกอบคลาสหรือไม่มีหากไม่ได้กำหนด
__name__ - ชื่อชั้น
__module__- ชื่อโมดูลที่กำหนดคลาสไว้ แอตทริบิวต์นี้คือ "__main__" ในโหมดโต้ตอบ
__bases__ - ทูเปิลว่างเปล่าที่อาจมีคลาสพื้นฐานตามลำดับที่เกิดขึ้นในรายการคลาสพื้นฐาน
สำหรับชั้นเรียนข้างต้นให้เราพยายามเข้าถึงคุณลักษณะเหล่านี้ทั้งหมด -
#!/usr/bin/python
class Employee:
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print "Total Employee %d" % Employee.empCount
def displayEmployee(self):
print "Name : ", self.name, ", Salary: ", self.salary
print "Employee.__doc__:", Employee.__doc__
print "Employee.__name__:", Employee.__name__
print "Employee.__module__:", Employee.__module__
print "Employee.__bases__:", Employee.__bases__
print "Employee.__dict__:", Employee.__dict__
เมื่อดำเนินการโค้ดด้านบนจะให้ผลลัพธ์ดังนี้ -
Employee.__doc__: Common base class for all employees
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: ()
Employee.__dict__: {'__module__': '__main__', 'displayCount':
<function displayCount at 0xb7c84994>, 'empCount': 2,
'displayEmployee': <function displayEmployee at 0xb7c8441c>,
'__doc__': 'Common base class for all employees',
'__init__': <function __init__ at 0xb7c846bc>}
การทำลายวัตถุ (การเก็บขยะ)
Python ลบอ็อบเจ็กต์ที่ไม่จำเป็น (ชนิดในตัวหรืออินสแตนซ์คลาส) โดยอัตโนมัติเพื่อเพิ่มพื้นที่หน่วยความจำ กระบวนการที่ Python เรียกคืนบล็อกหน่วยความจำที่ไม่ได้ใช้งานอีกต่อไปเป็นระยะเรียกว่า Garbage Collection
ตัวรวบรวมขยะของ Python ทำงานระหว่างการเรียกใช้โปรแกรมและจะถูกทริกเกอร์เมื่อจำนวนการอ้างอิงของวัตถุถึงศูนย์ จำนวนการอ้างอิงของออบเจ็กต์จะเปลี่ยนไปเมื่อจำนวนนามแฝงที่ชี้ไปที่วัตถุนั้นเปลี่ยนไป
จำนวนการอ้างอิงของออบเจ็กต์จะเพิ่มขึ้นเมื่อมีการกำหนดชื่อใหม่หรือวางไว้ในคอนเทนเนอร์ (รายการทูเพิลหรือพจนานุกรม) จำนวนการอ้างอิงของออบเจ็กต์จะลดลงเมื่อลบด้วยเดลการอ้างอิงถูกกำหนดใหม่หรือการอ้างอิงอยู่นอกขอบเขต เมื่อจำนวนการอ้างอิงของวัตถุถึงศูนย์ Python จะรวบรวมโดยอัตโนมัติ
a = 40 # Create object <40>
b = a # Increase ref. count of <40>
c = [b] # Increase ref. count of <40>
del a # Decrease ref. count of <40>
b = 100 # Decrease ref. count of <40>
c[0] = -1 # Decrease ref. count of <40>
โดยปกติคุณจะไม่สังเกตเห็นเมื่อตัวรวบรวมขยะทำลายอินสแตนซ์ที่ไม่มีที่มาและเรียกคืนพื้นที่ แต่คลาสสามารถใช้เมธอดพิเศษ__del __ () ที่เรียกว่า destructor ซึ่งถูกเรียกใช้เมื่ออินสแตนซ์กำลังจะถูกทำลาย วิธีนี้อาจใช้เพื่อล้างทรัพยากรที่ไม่ใช่หน่วยความจำที่ใช้โดยอินสแตนซ์
ตัวอย่าง
ตัวทำลาย __del __ () นี้พิมพ์ชื่อคลาสของอินสแตนซ์ที่กำลังจะถูกทำลาย -
#!/usr/bin/python
class Point:
def __init__( self, x=0, y=0):
self.x = x
self.y = y
def __del__(self):
class_name = self.__class__.__name__
print class_name, "destroyed"
pt1 = Point()
pt2 = pt1
pt3 = pt1
print id(pt1), id(pt2), id(pt3) # prints the ids of the obejcts
del pt1
del pt2
del pt3
เมื่อดำเนินการโค้ดด้านบนจะให้ผลลัพธ์ดังนี้ -
3083401324 3083401324 3083401324
Point destroyed
Note- ตามหลักการแล้วคุณควรกำหนดคลาสของคุณในไฟล์แยกต่างหากจากนั้นคุณควรนำเข้าในไฟล์โปรแกรมหลักของคุณโดยใช้คำสั่งนำเข้า
การสืบทอดคลาส
แทนที่จะเริ่มต้นใหม่คุณสามารถสร้างคลาสโดยรับมาจากคลาสที่มีอยู่ก่อนหน้านี้โดยระบุคลาสพาเรนต์ไว้ในวงเล็บหลังชื่อคลาสใหม่
คลาสย่อยจะสืบทอดคุณลักษณะของคลาสแม่และคุณสามารถใช้แอ็ตทริบิวต์เหล่านั้นราวกับว่าถูกกำหนดไว้ในคลาสลูก คลาสย่อยยังสามารถแทนที่สมาชิกข้อมูลและวิธีการจากผู้ปกครอง
ไวยากรณ์
คลาสที่ได้รับจะประกาศเหมือนกับคลาสแม่ของพวกเขา อย่างไรก็ตามรายการของคลาสพื้นฐานที่จะสืบทอดจะได้รับหลังจากชื่อคลาส -
class SubClassName (ParentClass1[, ParentClass2, ...]):
'Optional class documentation string'
class_suite
ตัวอย่าง
#!/usr/bin/python
class Parent: # define parent class
parentAttr = 100
def __init__(self):
print "Calling parent constructor"
def parentMethod(self):
print 'Calling parent method'
def setAttr(self, attr):
Parent.parentAttr = attr
def getAttr(self):
print "Parent attribute :", Parent.parentAttr
class Child(Parent): # define child class
def __init__(self):
print "Calling child constructor"
def childMethod(self):
print 'Calling child method'
c = Child() # instance of child
c.childMethod() # child calls its method
c.parentMethod() # calls parent's method
c.setAttr(200) # again call parent's method
c.getAttr() # again call parent's method
เมื่อดำเนินการโค้ดด้านบนจะให้ผลลัพธ์ดังนี้ -
Calling child constructor
Calling child method
Calling parent method
Parent attribute : 200
ในทำนองเดียวกันคุณสามารถขับเคลื่อนคลาสจากคลาสพาเรนต์หลาย ๆ คลาสได้ดังนี้ -
class A: # define your class A
.....
class B: # define your class B
.....
class C(A, B): # subclass of A and B
.....
คุณสามารถใช้ฟังก์ชัน issubclass () หรือ isinstance () เพื่อตรวจสอบความสัมพันธ์ของสองคลาสและอินสแตนซ์
issubclass(sub, sup) ฟังก์ชันบูลีนจะคืนค่าจริงหากคลาสย่อยที่กำหนด sub เป็นคลาสย่อยของซูเปอร์คลาส sup.
isinstance(obj, Class)ฟังก์ชันบูลีนจะคืนค่าจริงถ้าobjเป็นอินสแตนซ์ของคลาสคลาสหรือเป็นอินสแตนซ์ของคลาสย่อยของคลาส
วิธีการลบล้าง
คุณสามารถแทนที่เมธอดคลาสพาเรนต์ได้ตลอดเวลา เหตุผลหนึ่งในการลบล้างเมธอดของพาเรนต์เป็นเพราะคุณอาจต้องการฟังก์ชันพิเศษหรือแตกต่างกันในคลาสย่อยของคุณ
ตัวอย่าง
#!/usr/bin/python
class Parent: # define parent class
def myMethod(self):
print 'Calling parent method'
class Child(Parent): # define child class
def myMethod(self):
print 'Calling child method'
c = Child() # instance of child
c.myMethod() # child calls overridden method
เมื่อดำเนินการโค้ดด้านบนจะให้ผลลัพธ์ดังนี้ -
Calling child method
วิธีการโอเวอร์โหลดฐาน
ตารางต่อไปนี้แสดงฟังก์ชันทั่วไปบางอย่างที่คุณสามารถแทนที่ในชั้นเรียนของคุณเองได้ -
ซีเนียร์ | วิธีการคำอธิบายและการโทรตัวอย่าง |
---|---|
1 | __init__ ( self [,args...] ) ตัวสร้าง (พร้อมอาร์กิวเมนต์ที่เป็นทางเลือก) ตัวอย่างการโทร: obj = className (args) |
2 | __del__( self ) Destructor ลบวัตถุ โทรตัวอย่าง: del obj |
3 | __repr__( self ) การแสดงสตริงที่ประเมินได้ โทรตัวอย่าง: repr (obj) |
4 | __str__( self ) การแสดงสตริงที่พิมพ์ได้ โทรตัวอย่าง: str (obj) |
5 | __cmp__ ( self, x ) การเปรียบเทียบวัตถุ ตัวอย่างการโทร: cmp (obj, x) |
ตัวดำเนินการมากเกินไป
สมมติว่าคุณสร้างคลาส Vector เพื่อแทนเวกเตอร์สองมิติจะเกิดอะไรขึ้นเมื่อคุณใช้ตัวดำเนินการบวกเพื่อเพิ่ม Python ส่วนใหญ่จะตะโกนใส่คุณ
อย่างไรก็ตามคุณสามารถกำหนดเมธอด__add__ในคลาสของคุณเพื่อทำการบวกเวกเตอร์จากนั้นตัวดำเนินการบวกจะทำงานตามความคาดหวัง -
ตัวอย่าง
#!/usr/bin/python
class Vector:
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self):
return 'Vector (%d, %d)' % (self.a, self.b)
def __add__(self,other):
return Vector(self.a + other.a, self.b + other.b)
v1 = Vector(2,10)
v2 = Vector(5,-2)
print v1 + v2
เมื่อดำเนินการโค้ดด้านบนจะให้ผลลัพธ์ดังนี้ -
Vector(7,8)
การซ่อนข้อมูล
แอตทริบิวต์ของออบเจ็กต์อาจมองเห็นได้หรือไม่ก็ได้นอกนิยามคลาส คุณต้องตั้งชื่อแอตทริบิวต์ด้วยเครื่องหมายขีดล่างสองอันและบุคคลภายนอกจะมองไม่เห็นแอตทริบิวต์เหล่านั้นโดยตรง
ตัวอย่าง
#!/usr/bin/python
class JustCounter:
__secretCount = 0
def count(self):
self.__secretCount += 1
print self.__secretCount
counter = JustCounter()
counter.count()
counter.count()
print counter.__secretCount
เมื่อดำเนินการโค้ดด้านบนจะให้ผลลัพธ์ดังนี้ -
1
2
Traceback (most recent call last):
File "test.py", line 12, in <module>
print counter.__secretCount
AttributeError: JustCounter instance has no attribute '__secretCount'
Python ปกป้องสมาชิกเหล่านั้นโดยการเปลี่ยนชื่อภายในเพื่อรวมชื่อคลาส คุณสามารถเข้าถึงคุณลักษณะเช่นobject._className__attrName หากคุณจะแทนที่บรรทัดสุดท้ายของคุณดังต่อไปนี้แสดงว่าเหมาะสำหรับคุณ -
.........................
print counter._JustCounter__secretCount
เมื่อดำเนินการโค้ดด้านบนจะให้ผลลัพธ์ดังนี้ -
1
2
2