# 練習42.對象、類、以及從屬關系
有一個重要的概念你需要弄明白,那就是“類(class)”和“對象(object)”的區別。問題在于,class 和 object 并沒有真正的不同。它們其實是同樣的東西,只是在不同的時間名字不同罷了。我用禪語來解釋一下吧:
~~~
魚和三文魚有什么區別?
~~~
這個問題有沒有讓你有點暈呢?說真的,坐下來想一分鐘。我的意思是說,魚和三文魚是不一樣,不過它們其實也是一樣的是不是?三文魚是魚的一種,所以說沒什么不同,不過三文魚又有些特別,它和別的種類的魚的確不一樣,比如三文魚和大比目魚就不一樣。所以三文魚和魚既相同又不同。怪了。
這個問題讓人暈的原因是大部分人不會這樣去思考問題,其實每個人都懂這一點,你無須去思考魚和三文魚的區別,因為你知道它們之間的關系。你知道三文魚是魚的一種,而且魚還有別的種類,根本就沒必要去思考這類問題。
讓我們更進一步,假設你有一只水桶,里邊有三條三文魚。假設你的好人卡多到沒地方用,于是你給它們分別取名叫Frank, Joe,和Mary。現在想想這個問題:
~~~
Mary和三文魚有什么區別?
~~~
這個問題一樣的奇怪,但比起魚和三文魚的問題來還好點。你知道Mary是一條三文魚,所以他并沒什么不同,他只是三文魚的一個“實例(instance)”。Frank和Joe一樣也是三文魚的實例。我的意思是說,它們是由三文魚創建出來的,而且代表著和三文魚一樣的屬性。
所以我們的思維方式是(你可能會有點不習慣):魚是一個“類(class)”,三文魚是一個“類(class)”,而Mary是一個“對象(object)”。仔細想想,然后我再一點一點慢慢解釋給你。
魚是一個“類”,表示它不是一個真正的東西,而是一個用來描述具有同類屬性的實例的概括性詞匯。 你有鰭?你有鰾?你住在水里?好吧那你就是一條魚。
后來河蟹養殖專家路過,看到你的水桶,于是告訴你:“小伙子,你這些魚是三文魚。” 并且專家還定義了一個新的叫做“三文魚”的“類”,而這個“類”又有它特定的屬性。長鼻子?淺紅色的肉?生活在海洋里?吃起來味道還可以?那你就是一條三文魚。
最后一個廚師過來了,他跟專家說:“非也非也,你看到的是三文魚,我看到的是Mary,而且我要把Mary和剁椒配一起做一道小菜。”于是你就有了一只叫做Mary的三文魚的“實例(instance)”(三文魚也是魚的一個“實例”),并且你使用了它,這樣它就是一個“對象(object)”。
這會你應該了解了:Mary是三文魚的成員,而三文魚又是魚的成員。這里的關系式:對象屬于某個類,而某個類又屬于另一個類。
## 寫成代碼是什么樣子
這個概念有點繞,不過實話說,你只要在創建和使用 class 的時候操心一下就可以了。我來給你兩個區分 Class 和 Object 的小技巧。
首先針對類和對象,你需要學會兩個說法,“is-a(是啥)”和“has-a(有啥)”。“是啥”要用在談論“兩者以類的關系互相關聯”的時候,而“有啥”要用在“兩者無共同點,僅是互為參照”的時候。
接下來,通讀這段代碼,將每一個注釋為`##`?? 的位置標明他是“is-a”還是“has-a”的關系,并講明白這個關系是什么。在代碼的開始我還舉了幾個例子,所以你只要寫剩下的就可以了。
記住,“是啥”指的是魚和三文魚的關系,而“有啥”指的是三文魚和鰓的關系。
~~~
## Animal is-a object (yes, sort of confusing) look at the extra credit
class Animal(object):
pass
## ??
class Dog(Animal):
def __init__(self, name):
## ??
self.name = name
## ??
class Cat(Animal):
def __init__(self, name):
## ??
self.name = name
## ??
class Person(object):
def __init__(self, name):
## ??
self.name = name
## Person has-a pet of some kind
self.pet = None
## ??
class Employee(Person):
def __init__(self, name, salary):
## ?? hmm what is this strange magic?
super(Employee, self).__init__(name)
## ??
self.salary = salary
## ??
class Fish(object):
pass
## ??
class Salmon(Fish):
pass
## ??
class Halibut(Fish):
pass
## rover is-a Dog
rover = Dog("Rover")
## ??
satan = Cat("Satan")
## ??
mary = Person("Mary")
## ??
mary.pet = satan
## ??
frank = Employee("Frank", 120000)
## ??
frank.pet = rover
## ??
flipper = Fish()
## ??
crouse = Salmon()
## ??
harry = Halibut()
~~~
## 關于 class Name(object)
記得我曾經強迫讓你使用`class Name(object)` 卻沒告訴你為什么吧,現在你已經知道了“類”和“對象”的區別,我就可以告訴你原因了。如果我早告訴你的話,你可能會暈掉,也學不會這門技術了。
真正的原因是在 Python 早期,它對于`class` 的定義在很多方面都是嚴重有問題的。當他們承認這一點的時候已經太遲了,所以逼不得已,他們需要支持這種有問題的 `class`。為了解決已有的問題,他們需要引入一種“新類”,這樣的話“舊類”還能繼續使用,而你也有一個新的正確的類可以使用了。
這就用到了“類即是對象”的概念。他們決定用小寫的“object”這個詞作為一個類,讓你在創建新類時從它繼承下來。有點暈了吧?一個類從另一個類繼承,而后者雖然是個類,但名字卻叫“object”……不過在定義類的時候,別忘記要從 object 繼承就好了。
的確如此。一個詞的不同就讓這個概念變得更難理解,讓我不得不現在才講給你。現在你可以試著去理解“一個是對象的類”這個概念了,如果你感興趣的話。
不過我還是建議你別去理解了,干脆完全忘記舊格式和新格式類的區別吧,就假設 Python 的 class 永遠都要求你加上 (object) 好了,你的腦力要留著思考更重要的問題。
## 附加題
> 1. 研究一下為什么Python添加了這個奇怪的叫做`object`的類,它究竟有什么含義呢?
> 1. 有沒有辦法把`Class`當作`Object`使用呢?
> 1. 在習題中為 animals、fish、還有 people 添加一些函數,讓它們做一些事情。看看當函數在 Animal 這樣的“基類(base class)”里和在 Dog 里有什么區別。
> 1. 找些別人的代碼,理清里邊的“是啥”和“有啥”的關系。
> 1. 使用列表和字典創建一些新的一對應多的“has-many”的關系。
> 1. 你認為會有一種“has-many”的關系嗎?閱讀一下關于“多重繼承(multiple inheritance)”的資料,然后盡量避免這種用法。
## 常見問題
### Q: 這些注釋符`## ??`是干什么的?
> 這些是你需要完成的“填空”,你需要填上 "is-a"或者 "has-a"。再讀一遍本節練習,看看其他的注釋,弄明白我再說什么。
### Q: `self.pet = None`是什么意思?
> 確保給`self.pet`設置了一個默認值`None`
### Q: `super(Employee, self).__init__(name)`實現了什么?
> 這是用來執行父類的`__init__`方法的,上網搜索一下“python super”相關文檔,閱讀文檔的各種建議。
- 序言
- 前言
- 簡介
- 0:安裝和準備
- 1:第一個程序
- 2:注釋和“#”井號
- 3:數字和數學計算
- 4:變量和命名
- 5:更多的變量和打印
- 6:字符串和文本
- 7:更多的打印(輸出)
- 8:打印, 打印
- 9:打印, 打印, 打印
- 10:那是什么?
- 11:提問
- 12:提示別人
- 13:參數, 解包, 變量
- 14:提示和傳遞
- 15:讀文件
- 16:讀寫文件
- 17:更多文件操作
- 18:命名, 變量, 代碼, 函數
- 19:函數和變量
- 20:函數和文件
- 21:函數的返回值
- 22:到目前為止你學到了什么?
- 23:閱讀代碼
- 24:更多的練習
- 25:更多更多的練習
- 26:恭喜你,可以進行一次考試了
- 27:記住邏輯
- 28:布爾表達式
- 29:IF 語句
- 30:Else 和 If
- 31:做出決定
- 32:循環和列表
- 33:while循環
- 34:訪問列表元素
- 35:分支和函數
- 36:設計和調試
- 37:復習符號
- 38:列表操作
- 39:字典,可愛的字典
- 40:模塊, 類和對象
- 41:學會說面向對象
- 42:對象、類、以及從屬關系
- 43:基本的面向對象的分析和設計
- 44:繼承Vs.包含
- 45:你來制作一個游戲
- 46:項目骨架
- 47:自動化測試
- 48:更復雜的用戶輸入
- 49:寫代碼語句
- 50:你的第一個網站
- 51:從瀏覽器獲取輸入
- 52:開始你的web游戲
- 來自老程序員的建議
- 下一步
- 附錄A:命令行教程
- 簡介
- 安裝和準備
- 路徑, 文件夾, 名錄 (pwd)
- 如果你迷路了
- 創建一個路徑 (mkdir)
- 改變當前路徑 (cd)
- 列出當前路徑 (ls)
- 刪除路徑 (rmdir)
- 目錄切換(pushd, popd)
- 生成一個空文件(Touch, New-Item)
- 復制文件 (cp)
- 移動文件 (mv)
- 查看文件 (less, MORE)
- 輸出文件 (cat)
- 刪除文件 (rm)
- 退出命令行 (exit)
- 下一步