天天看點

第4章 面向對象程式設計面向對象程式設計

面向對象程式設計

基礎知識

如果某類派生自其他基類的話則需要把所有基類放到一對圓括号中并使用逗号分隔,然後是一個冒号,最後換行并定義類的内部實作。

eg:class Car():

   def infor(self):

   print(“This is a car”)

定義了類之後,就可以用來執行個體化對象,并通過“對象名.成員”的方法來通路其中的資料成員或成員方法。

eg:car=Car()

   car.infor()

   This is a car

Python提供了一個關鍵字pass,執行的時候什麼也不會發生什麼,可以用在類和函數的定義中或者選擇結構中,表示空語句。

class A:

   pass

可以用三引号為類進行必要的注釋

calss Test:

   ‘’‘This is only a test’’’

私有成員與公有成員

在定義類的成員時,如果成員名以兩個下劃線(_ _)開頭則表示是私有成員。

私有成員在類的外部不能直接通路,一般在類的内部進行通路和操作,或者在類外部通過調用對象的公有成員方法來通路。

Python提供了一種特殊方式"對象名._類名 _ _xxx"可以通路私有成員,但這會破壞類的封裝性。

以下畫線開頭和結束的成員名有特殊的含義,類中使用下畫線作為變量名和方法名字首和字尾來表示類的特殊成員。

  • _xxx:保護成員,不能用‘from module import *'導入,隻有類對象和子類對象可以通路這些成員。
  • _ _xxx _ _:系統定義的特殊成員。
  • _ _xxx:類中的私有成員,一般隻有類對象自己能通路,子類對象也不能通路到這個成員,但在對象外部可以通過“對象名. _ 類名 _ _xxx"這樣的特殊方式來通路。

資料成員

資料成員可以分為兩大類:屬于對象的資料成員和屬于類的資料成員。

  • 屬于對象的資料成員主要指在構造函數_ _init() _ _ 中定義的(當然也可以在其他成員方法中定義),定義和使用時必須以self作為字首(這一點是必須的),同一個類的不同對象(執行個體)之間的資料成員之間互不影響。
  • 屬于類的資料成員是該類所有對象共享的,不屬于任何一個對象,在定義類時這類資料成員不在任何一個成員方法的定義中。
  • 在主程式中或類的外部,對象資料成員屬于執行個體(對象),隻能通過對象名通路。
  • 類資料成員屬于類,可以通過類名或對象名通路。
  • 在Python中可以動态地為類和對象增加成員。

    eg:class Car(object):

        price=100000 #屬于類的資料成員

        def init(self,c):

          self.color=c #屬于對象的資料成員

       car1=Car(“Red”)#執行個體化對象

       print(car1.color,Car.price) #通路對象和類的資料成員

       Car.price=110000 #修改類的屬性

       

       car1.color=“Yellow” #修改執行個體的屬性

       def setSpeed(self,s):

         self.speed=s

       import types

       car1.setSpeed=types.MethodType(setSpeed,car1) #動态為對象增加成員方法

       car1.setSpeed(50)

方法

    在類中定義的方法可以粗略分為四大類:公有方法、私有方法、靜态方法和類方法。

       每個對象都有自己的公有方法和私有方法,在這兩類方法中都可以通路屬于類和對象的成員。

       公有方法通過對象名直接調用,私有方法不能通過對象名直接調用,隻能在執行個體方法中通過self調用或在外部通過Python支援的特殊方式調用。

       類的所有執行個體方法都必須至少有一個名為self的參數,并且必須是方法的第一個形參(如果有多個形參的話),self參數代表對象自身。

       在類的執行個體方法中通路執行個體屬性時需要以self為字首,但在外部通過對象名調用對象方法時并不需要傳遞這個參數,如果在外部通過類名調用屬于對象的公有方法,需要顯示為該方法的self參數傳遞一個對象名,用來明确指定通路哪個對象的資料成員。

       靜态方法和類方法都可以通過類名和對象名調用,但不能直接通路屬于對象的成員,隻能通路類的成員。一般将cls作為類方法的第一個參數,表示該類自身,在調用類方法時不需要為該參數傳遞值。

eg:

class Root:

      __total=0

       def init(self,v): #構造函數

           self.__value=v

           Root.__total+=1

       def show(self):

         print(‘self.__value:’,self.__value)

         print(‘Root.__total’,Root.__total)

       @classmethod #修飾器,聲明靜态方法

       def classShowTotal(cls): #類方法

          print(cls.__total)

       @staticmethod #修飾器,聲明靜态方法

       def staticShowTotal(): #靜态方法

          print(Root.__total)

r=Root(3)

r.classShowTotal()

r.staticShowTotal()

r.show()

rr=Root(5)

Root.classShowTotal()

Root.staticShowTotal()

Root.show ( r ) #通過類名調用屬于對象的公有方法,需要顯示為該方法的self參數傳遞

不同對象執行個體的資料成員之間互不影響,是不共享的。但在同一個類的所有執行個體方法
是在不同對象之間共享的,所有對象都執行相同的代碼,通過self參數來判斷要處理
哪個對象資料。
           

屬性

     公開的資料成員可以在外部随意通路和修改,很難控制使用者修改時新資料的合法性。解決這一問題的常用方法是常用是定義私有資料成員,然後設計公開的成員方法來提供對私有資料成員的健壯性,保證了資料的完整性。

     屬性結合了公開資料成員和成員方法的優點,既可以像成員方法那樣對值進行必要的檢查,又可以像資料成員一樣靈活地通路。

eg:

  • class Test:

         def init(self,value):

         self._ _value=value

         @property #修飾器,定義屬性,提供對私有資料成員的通路

         def value(self): #隻讀屬性,無法修改和删除

               return self._ _value

      t=Test(3)

class Test:

    def init(self,value):

      self.__value=value

     def __get(self): #讀取私有資料成員的值

      return self.__value

    def __set(self,v): #修改私有資料成員的值

      self.__value=v

     value=property(__get,__set) #可讀可寫屬性,指定相應的讀寫方法

    def show(self):

      print(self.__value)

  t=Test(3)

  print(t.value) #允許讀取屬性值

  t.value=5 #允許修改屬性值

  print(t.value)

  t.show() #屬性對應的私有變量也得到了相應的修改

繼承

  派生類可以繼承父類的公有成員,但是不能繼承其私有成員。

如果需要在派生類中調用基類的方法,可以使用内置函數super()或者通過“基類名.方法名()"的方式來實作這一這一目的。

  

eg:

class Person(object):

  #基類必須繼承于object,否則在派生類中将無法使用super()函數

   def init(self,name=’’,age=20,sex=‘man’):

    self.setName(name) #通過調用方法進行初始化

    self.setAge(age) #這樣可以對參數進行更好地控制

    self.setSex(sex)

  def setName(self,name):

     if not isinstance(name,str):

      print(‘name must be string’)

      self.__name=’’

      return

    self.__name=name

  def setAge(self,age):

     if type(age)!=int:

      print(‘age must be interger.’)

      self.__age=20

      return

     self.__age=age

  def setSex(self,sex):

     if sex not in(‘man’,‘woman’):

       print(‘sex must be "man"or “woman”’)

       self.__sex=‘man’

       return

     self.__sex=sex

  def show(self):

    print(self.__name,self.__age,self.__sex)

#派生類

class Teacher(Person):

  def init(self,name=’’,age=30,sex=‘man’,deparment=‘Computer’):

#調用基類的構造方法初始化基類的私有資料成員

     super(Teacher,self).init(name,age,sex)

#也可以這樣初始化基類的私有資料成員

    #Person.init(self,name,age,sex)

#初始化派生類的資料成員

    self.setDepartment(deparment)

  def setDepartment(self,department):

    if type(department)!=str:

      print(‘deparement must be a string.’)

      self.__department=‘Computer’

      return

    self.__department=department

  def show(self):

    super(Teacher,self).show()

    print(self.__department)

if name==‘main’:

     #建立基類對象

    zhangsan=Person(‘Zhang San’,19,‘man’)

    zhangsan.show()

    print(’=’*30)

     #建立派生類對象

    lisi=Teacher(‘LiSi’,32,‘man’,‘Math’)

    lisi.show()

    #調用繼承的方法修改年齡

    lisi.setAge(40)

    lisi.show()

Python支援多繼承,如果父類中有相同的方法名,而在子類中使用沒有
    指定父類名,則Python解釋器将從左向右按順序進行搜素。
           

下面代碼完整地描述了類的繼承機制,請認真體會構造函數、私有方法以及普通公開方法的繼承原理。

eg:

class A():

  def init(self):

     self.__private()

     self.public()

  def __private(self):

     print(’__private()method of A’)

  def public(self):

     print(‘public()method of A’)

  def __test(self):

      print(’__test()method of A’)

class B(A): #注意,B類沒有構造函數

  def __privatr(self):

     print(’__private()method of B’)

  def public(self):

     print(‘public()method of B’)

b=B() #建立派生類對象

#b.test() #派生類沒有繼承基類的私有成員方法

class C(A):

  def init(self):

      self.__private()

     self.public()

  def __private(self):

     print(’__private()method of C’)

  def public(self):

     print(‘public()method of C’)

c=C()

特殊方法與運算重載

  構造函數是_ _init() _ _,一般用來為資料成員設定初始值或進行其他必要的初始化工作,在建立對象時被自動調用時和執行。如果使用者沒有設計構造函數,Python中的析構函數是 _ _del _ _(),一般用來釋放對象占有的資源,在Python删除對象和回收對象空間時被自動調用和執行。

  如果使用者沒有編寫析構函數,Python将提供一個預設的析構函數進行必要的清理工作。

  在Python中,除了構造函數和析構函數之外,還有大量的特殊方法支援更多的功能,運算符重載就是通過在類中重寫特殊函數來實作的。

  在自定義時如果重寫了某個特殊方法即可支援對應的運算符,具體實作什麼工作則完全可以根據需要來定義。

_ _ new_ _() 類的靜态方法,用于确定是否要建立對象
_ _init _ _() 構造函數,生成對象時調用
_ _del _ _() 析構函數,生成對象時調用
_ add_ _() +
_ _sub _ _() -
_ mul _ *
_ _truediv _ _() /
_ _floordiv _ _() //
_ _mod _ _() %
_ _pow _ _() **
_ _repr _ _() 列印、轉換
_ _ setitem _ _() 按照索引指派
_ _getitem _ _() 按照索引取值
_ _len() _ _ 計算長度
_ _ call _ _() 函數調用
_ _contains _ _ in
_ _eq _ _()、 _ _ ne _ _()、 _ _ lt _ _()、 _ _le _ _()、 _ _gt _ _()、 _ _ge _ _() ==、!=、<、<=、>、>=
_ _ str _ _() 轉換為 字元串
_ _ lshift _ _()、 _ _rshift _ _() <<、>>
_ _and _ _()、 _ _or _ _()、 _ _invert _ _()、 _ _xor _ _() &、l、~、^
_ _ iadd _ _()、 _ _ isub _ _() +=、-=