課時39:類與物件:拾遺

那是個好男孩發表於2018-08-23

目錄:

  一、組合

  二、類、類物件和例項物件

  三、到底什麼是繫結

  四、課時39課後習題及答案

 

***********

一、組合

***********

 現在要求定義一個類,叫水池,水池裡要有烏龜和魚。

在Python裡其實很簡單,直接把需要的類放進去例項化就可以了,這就叫組合:

#p11_3.py
class Turtle:
      def __init__(self, x):
            self.num = x

class Fish:
      def __init__(self, x):
            self.num = x

class Pool:
      def __init__(self, x, y):
            self.turtle = Turtle(x)
            self.fish = Fish(y)
      def print_num(self):
            print("水池裡總共有烏龜 %d 只,小魚 %d 條!" % (self.turtle.num, self.fish.num))
>>> #先執行p11_3.py
>>> pool = Pool(1,10)
>>> pool.print_num()
水池裡總共有烏龜 1 只,小魚 10 條!

Python的特性其實還支援另外一種很流行的程式設計模式:Mixin.【擴充套件閱讀】Mixin程式設計機制(https://fishc.com.cn/forum.php?mod=viewthread&tid=48888&extra=page%3D1%26filter%3Dtypeid%26typeid%3D403

 

*******************************

二、類、類物件和例項物件

*******************************

 先來分析一段程式碼:

>>> class C:
    count = 0

    
>>> a = C()
>>> b = C()
>>> c = C()
>>> print(a.count,b.count,c.count)
0 0 0
>>> c.count += 10
>>> print(a.count,b.count,c.count)
0 0 10
>>> C.count += 10
>>> print(a.count,b.count,c.count)
10 10 10
>>> C.count += 100
>>> print(a.count,b.count,c.count)
110 110 10

從上面的例子可以看出,對例項物件c的count屬性進行賦值後,就相當於覆蓋了類物件C的count屬性。如圖所示,如果沒有賦值覆蓋,那麼引用的是類物件的count屬性。

 

需要注意的是,類中定義的屬性是靜態變數,也就是相當於C語言中加上static關鍵字宣告變數,類的屬性是與類物件進行繫結,並不會依賴任何它的例項物件。

另外,如果屬性的名字和方法名相同,屬性會覆蓋方法:

>>> class C:
    def x(self):
        print(`Xman`)

        
>>> c = C()
>>> c.x()
Xman
>>> c.x = 1
>>> c.x
1
>>> c.x()
Traceback (most recent call last):
  File "<pyshell#21>", line 1, in <module>
    c.x()
TypeError: `int` object is not callable

為了避免名字上的衝突,大家應該遵守一些約定俗成的規定:

(1)類的定義要“少吃多餐”,不要試圖在一個類裡邊定義出所有能想到的特性和方法,應該利用繼承和組合機制來進行擴充套件。

(2)用 不同的詞性命名,如屬性名用名詞、方法名用動詞,並使用駱駝命名法

 

***********************

三、到底什麼是繫結

***********************

 Python嚴格要求方法需要用例項才能呼叫,這種限制其實就是Python所謂的繫結概念。前面也粗略地解釋了一下繫結,但有可能你會這麼嘗試,然後發現也可以呼叫:

>>> class BB:
    def printBB():
        print("no zuo no die")


>>> BB.printBB()
no zuo no die

但是這樣做有個問題,就是根據類的例項化後的物件根本無法呼叫裡邊的函式:

>>> bb = BB()
>>> bb.printBB()
Traceback (most recent call last):
  File "<pyshell#29>", line 1, in <module>
    bb.printBB()
TypeError: printBB() takes 0 positional arguments but 1 was given

實際上由於Python的繫結機制,這裡自動把bb物件作為第一個引數傳入,所以才會出先TypeError。

為了更好的理解,再深入的挖一挖:

>>> class CC:
    def setXY(self,x,y):
        self.x = x
        self.y = y
    def printXY(self):
        print(self.x,self.y)

>>> dd = CC()

可以使用_ _dict_ _檢視物件所擁有的屬性:

>>> dd.__dict__
{}
>>> CC.__dict__
mappingproxy({`__module__`: `__main__`, `setXY`: <function CC.setXY at 0x0000024E3998D400>, `printXY`: <function CC.printXY at 0x0000024E3998D620>, `__dict__`: <attribute `__dict__` of `CC` objects>, `__weakref__`: <attribute `__weakref__` of `CC` objects>, `__doc__`: None})

_ _dict_ _屬性是由一個字典組成,字典中僅有例項物件的屬性,不現實類屬性和特殊屬性,鍵表示的是屬性名,值表示屬性相應的資料值。

>>> dd.setXY(4,5)
>>> dd.__dict__
{`x`: 4, `y`: 5}

現在例項物件dd有了兩個新屬性,而且這兩個屬性僅屬於例項物件的:

>>> CC.__dict__
mappingproxy({`__module__`: `__main__`, `setXY`: <function CC.setXY at 0x0000024E3998D400>, `printXY`: <function CC.printXY at 0x0000024E3998D620>, `__dict__`: <attribute `__dict__` of `CC` objects>, `__weakref__`: <attribute `__weakref__` of `CC` objects>, `__doc__`: None})

為什麼會這樣呢?完全歸功於self引數:當例項物件dd去呼叫setXY方法的時候,它傳入的第一個引數是dd,那麼self.x = 4,self.y = 5也就相當於dd.x = 4,dd.y = 5,所以你在例項物件,甚至類物件中都看不到x和y,因為這兩個屬性是隻屬於例項物件dd的。

接下來思考下:如果把類例項刪除掉,例項物件dd還能否呼叫printXY方法?

>>> del CC
        答案是可以的:
>>> dd.printXY()
4 5

 

*******************************

四、課時39課後習題及答案

*******************************

 

 

相關文章