目錄:
一、組合
二、類、類物件和例項物件
三、到底什麼是繫結
四、課時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課後習題及答案
*******************************