Python面向對象(類和對象)


自從存在以來,Python一直是面向對象的語言。 因此,創建和使用類和對象是非常容易的。 本章將學習如何使用Python面向對象編程。

如果您以前沒有面向對象(OO)編程的經驗,可能需要查閱介紹面向對象(OO)編程課程或至少學習一些有關教程,以便掌握基本概念。

下面是面向對象編程(OOP)的一個小介紹,以幫助您快速入門學習 -

OOP術語概述

  • - 用於定義表示用戶定義對象的一組屬性的原型。屬性是通過點符號訪問的數據成員(類變量和實例變量)和方法。
  • 類變量 - 由類的所有實例共享的變量。 類變量在類中定義,但在類的任何方法之外。 類變量不像實例變量那樣頻繁使用。
  • 數據成員 - 保存與類及其對象相關聯的數據的類變量或實例變量。
  • 函數重載 - 將多個行爲分配給特定函數。 執行的操作因涉及的對象或參數的類型而異。
  • 實例變量 - 在方法中定義並僅屬於類的當前實例的變量。
  • 繼承 - 將類的特徵傳遞給從其派生的其他類。
  • 實例 - 某個類的單個對象。 例如,對象obj屬於Circle類,它是Circle類的實例。
  • 實例化 - 創建類的實例。
  • 方法 - 在類定義中定義的一種特殊類型的函數。
  • 對象 - 由其類定義的數據結構的唯一實例。對象包括數據成員(類變量和實例變量)和方法。
  • 運算符重載 - 將多個函數分配給特定的運算符。

1.創建類

class語句創建一個新的類定義。 類的名稱緊跟在class關鍵字之後,在類的名稱之後緊跟冒號,如下 -

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 __()是一種特殊的方法,當創建此類的新實例時,該方法稱爲Python構造函數或初始化方法。

  • 聲明其他類方法,如正常函數,但每個方法的第一個參數是self。 Python將self參數添加到列表中; 調用方法時不需要包含它。

2.創建實例對象

要創建類的實例,可以使用類名調用該類,並傳遞其__init__方法接受的任何參數。

## This would create first object of Employee class
emp1 = Employee("Maxsu", 2000)
## This would create second object of Employee class
emp2 = Employee("Kobe", 5000)

3.訪問屬性

可以使用帶有對象的點(.)運算符來訪問對象的屬性。 類變量將使用類名訪問如下 -

emp1.displayEmployee()
emp2.displayEmployee()
print ("Total Employee %d" % Employee.empCount)

現在把所有的概念放在一起 -

#!/usr/bin/python3

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("Maxsu", 2000)
#This would create second object of Employee class"
emp2 = Employee("Kobe", 5000)
emp1.displayEmployee()
emp2.displayEmployee()
print ("Total Employee %d" % Employee.empCount)

當執行上述代碼時,會產生以下結果 -

Name :  Maxsu ,Salary:  2000
Name :  Kobe ,Salary:  5000
Total Employee 2

可以隨時添加,刪除或修改類和對象的屬性 -

emp1.salary = 7000  # Add an 'salary' attribute.
emp1.name = 'xyz'  # Modify 'age' attribute.
del emp1.salary  # Delete 'age' attribute.

如果不是使用普通語句訪問屬性,可以使用以下函數 -

  • getattr(obj,name [,default]) - 訪問對象的屬性。
  • hasattr(obj,name) - 檢查屬性是否存在。
  • setattr(obj,name,value) - 設置一個屬性。如果屬性不存在,那麼它將被創建。
  • delattr(obj,name) - 刪除一個屬性。

下面是一此使用示例 -

hasattr(emp1, 'salary')    # Returns true if 'salary' attribute exists
getattr(emp1, 'salary')    # Returns value of 'salary' attribute
setattr(emp1, 'salary', 7000) # Set attribute 'salary' at 7000
delattr(emp1, 'salary')    # Delete attribute 'salary'

3.內置類屬性

每個Python類保持以下內置屬性,並且可以像任何其他屬性一樣使用點運算符訪問它們 -

  • __dict__ - 包含該類的命名空間的字典。
  • __doc__ - 類文檔字符串或無,如果未定義。
  • __name__ - 類名。
  • __module__ - 定義類的模塊名稱。此屬性在交互模式下的值爲「main」。
  • __bases__ - 一個包含基類的空元組,按照它們在基類列表中出現的順序。

對於上述類,嘗試訪問所有這些屬性 -

#!/usr/bin/python3

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)

emp1 = Employee("Maxsu", 2000)
emp2 = Employee("Bryant", 5000)
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__: (<class 'object'>,)
Employee.__dict__: {
   'displayCount': <function Employee.displayCount at 0x0160D2B8>, 
   '__module__': '__main__', '__doc__': 'Common base class for all employees', 
   'empCount': 2, '__init__': 
   <function Employee.__init__ at 0x0124F810>, 'displayEmployee': 
   <function Employee.displayEmployee at 0x0160D300>,
   '__weakref__': 
   <attribute '__weakref__' of 'Employee' objects>, '__dict__': 
   <attribute '__dict__' of 'Employee' objects>
}

4.銷燬對象(垃圾收集)

Python自動刪除不需要的對象(內置類型或類實例)以釋放內存空間。 Python定期回收不再使用的內存塊的過程稱爲垃圾收集。

Python的垃圾收集器在程序執行期間運行,當對象的引用計數達到零時觸發。 對象的引用計數隨着指向它的別名數量而變化。

當對象的引用計數被分配一個新名稱或放置在容器(列表,元組或字典)中時,它的引用計數會增加。 當用del刪除對象的引用計數時,引用計數減少,其引用被重新分配,或者其引用超出範圍。 當對象的引用計數達到零時,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__(),該方法在實例即將被銷燬時被調用。 此方法可能用於清理實例使用的任何非內存資源。

示例

這個__del__()析構函數打印要被銷燬的實例的類名 -

#!/usr/bin/python3

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

注意 - 理想情況下,應該在單獨的文件中定義類,然後使用import語句將其導入主程序文件。

在上面的例子中,假定Point類的定義包含在point.py中,並且其中沒有其他可執行代碼。

#!/usr/bin/python3
import point
p1 = point.Point()

5.類繼承

使用類繼承不用從頭開始構建代碼,可以通過在新類名後面的括號中列出父類來從一個預先存在的類派生它來創建一個類。

子類繼承其父類的屬性,可以像子類中一樣定義和使用它們。子類也可以從父類代替代數據成員和方法。

語法

派生類被聲明爲很像它們的父類; 然而,繼承的基類的列表在類名之後給出 -

class SubClassName (ParentClass1[, ParentClass2, ...]):
   'Optional class documentation string'
   class_suite

示例

#!/usr/bin/python3

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 calss B
.....

class C(A, B):   # subclass of A and B
.....

可以使用issubclass()isinstance()函數來檢查兩個類和實例之間的關係。

  • issubclass(sub,sup)布爾函數如果給定的子類sub確實是超類sup的子類返回True
  • isinstance(obj,Class)布爾函數如果obj是類Class的一個實例,或者是類的一個子類的實例則返回True

重載方法

可以隨時重載父類的方法。 重載父方法的一個原因是:您可能希望在子類中使用特殊或不同的方法功能。

示例

#!/usr/bin/python3

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 ) 析構函數,刪除一個對象 del obj
3 __repr__( self ) 可評估求值的字符串表示 repr(obj)
4 __str__( self ) 可打印的字符串表示 str(obj)
5 __cmp__ ( self, x ) 對象比較 cmp(obj, x)

6.重載運算符

假設已經創建了一個Vector類來表示二維向量。當使用加號(+)運算符執行運算時,它們會發生什麼? 很可能Python理解不了你想要做什麼。

但是,可以在類中定義__add__方法來執行向量加法,然後將按照期望行爲那樣執行加法運算 -

示例

#!/usr/bin/python3

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)

7.數據隱藏

對象的屬性在類定義之外可能或不可見。需要使用雙下劃線前綴命名屬性,然後這些屬性將不會直接對外部可見。

示例

#!/usr/bin/python3

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