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