笨方法學Python習題 45: 物件、類、以及從屬關係
程式碼1:
## Animal is-a object (yes, sort of confusing) look at the extra credit
class Animal(object):
pass
## Dog is-a class
class Dog(Animal):
def __init__(self, name):
## Animal has-a object
self.name = name
## Cat is-a class
class Cat(Animal):
def __init__(self, name):
## Cat has-a object
self.name = name
## Person is-a class
class Person(object):
def __init__(self, name):
## Person has-a object
self.name = name
## Person has-a pet of some kind
self.pet = None
## Employee is-a class
class Employee(Person):
def __init__(self, name, salary):
## ?? hmm what is this strange magic?
super(Employee, self).__init__(name)
## Employee has-a object
self.salary = salary
## Fish is-a object
class Fish(object):
pass
## Salmon is-a class
class Salmon(Fish):
pass
## Halibut is-a class
class Halibut(Fish):
Pass
## rover is-a Dog
rover = Dog("Rover")
## satan is-a Cat
satan = Cat("Satan")
## mary is-a Person
mary = Person("Mary")
## mary has-a pet
mary.pet = satan
## frank is-a Employee
frank = Employee("Frank", 120000)
## frank has-a pet
frank.pet = rover
## flipper is-a Fish
flipper = Fish()
## crouse is-a Salmon
crouse = Salmon()
## harry is-a Halibut
harry = Halibut()
加分習題
1. 研究一下為什麼 Python 添加了這個奇怪的叫做 object 的 class,它究竟有什麼
含義呢?
有沒有辦法把 Class 當作 Object 使用呢?
把已經定義的基類就可以作為新定義class的object
類似於:
class A(object):
pass
class B(A):
pass在習題中為 animals、 fish、還有 people 新增一些函式,讓它們做一些事情。看看
當函式在 Animal 這樣的“基類(base class)”裡和在 Dog 裡有什麼區別。找些別人的程式碼,理清裡邊的“是啥”和“有啥”的關係。
使用列表和字典建立一些新的一對應多的“has-many”的關係。
你認為會有一種“has-many”的關係嗎?閱讀一下關於“多重繼承(multiple
inheritance)”的資料,然後儘量避免這種用法。
多重繼承裡面容易出現重複呼叫的情況。比如說圖的右邊程式碼明顯會呼叫A和D兩次。為了避免這種情況,可以使用左邊的程式碼,參考註釋中標明change和new的部分。
他們的執行結果如下:
MRO(類繼承的順序)我查了資料以後的發現super.init()函式並不是一定呼叫父類函式,它其實是一個繼承的順序標記。
搬運某個大神的理解“
super 其實幹的是這事
def super(cls, inst):
mro = inst.class.mro()
return mro[mro.index(cls) + 1]
引數 cls 和 inst 分別做了兩件事:
1. inst 負責生成 MRO 的 list
2. 通過 cls 定位當前 MRO 中的 index, 並返回 mro[index + 1]
這兩件事才是 super 的實質,一定要記住!
MRO 全稱 Method Resolution Order,它代表了類繼承的順序。
舉個例子
class Root(object):
def __init__(self):
print("this is Root")
class B(Root):
def __init__(self):
print("enter B")
# print(self) # this will print <__main__.D object at 0x...>
super(B, self).__init__()
print("leave B")
class C(Root):
def __init__(self):
print("enter C")
super(C, self).__init__()
print("leave C")
class D(B, C):
pass
d = D()
print(d.__class__.__mro__)
輸出
enter B
enter C
this is Root
leave C
leave B
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.Root'>, <type 'object'>)
知道了 super 和父類其實沒有實質關聯之後,我們就不難理解為什麼 enter B 下一句是 enter C 而不是 this is Root(如果認為 super 代表“呼叫父類的方法”,會想當然的認為下一句應該是this is Root)。流程如下,在 B 的 init 函式中:
super(B, self).init()
首先,我們獲取 self.class.mro,注意這裡的 self 是 D 的 instance 而不是 B 的
然後,通過 B 來定位 MRO 中的 index,並找到下一個。顯然 B 的下一個是 C。於是,我們呼叫 C 的 init,打出 enter C。
順便說一句為什麼 B 的 init 會被呼叫:因為 D 沒有定義 init,所以會在 MRO 中找下一個類,去檢視它有沒有定義 init,也就是去呼叫 B 的 init。”
其實這一切邏輯還是很清晰的,關鍵是理解 super 到底做了什麼。
於是,MRO 中類的順序到底是怎麼排的呢?Python’s super() considered super!中已經有很好的解釋,我翻譯一下:
在 MRO 中,基類永遠出現在派生類後面,如果有多個基類,基類的相對順序保持不變。
大神的文章地址:https://laike9m.com/blog/li-jie-python-super,70/