# 練習40.模塊, 類和對象
Python 是一門“面向對象編程語言”。這意味著在Python中有一個叫做類的概念,你能通過類用一種特殊的方式構建你的軟件。使用類的概念,能給你的程序增添一致性,這樣你可以用一種很輕松方便的方式調用他們。至少,這是面向對象的理論。
我現在要通過使用你已經知道的字典和模塊等來教你開始學習面向對象編程、類和對象。我的問題是面向對象編程(OOP)只是普通的怪異。你要為之而奮斗,努力嘗試理解我講的內容,編寫代碼,在下一個練習中,我會更深入的講解。
我們要開始了。
## 模塊就像字典
你知道字典是如何被創建以及使用的,它用來將一個事物對應到另一個事物。意思就是說如果你有一個字典,字典中包括一個key "apple",那么你就可以實現:
~~~
mystuff = {'apple': "I AM APPLES!"}
print mystuff['apple']
~~~
記住“從X獲取Y”的說法,然后想一想模塊(module)。你之前已經寫過一些模塊,你應該知道它們:
> 1. 包含一些函數和變量的python文件
> 1. 你可以導入這個文件
> 1. 你可以用`.`操作符訪問這個模塊的函數和變量
想象一下我有一個叫做`mystuff.py`的模塊,在這個模塊中有一個叫做`apple`的函數,下面是 `mystuff.py` 模塊的代碼:
~~~
# this goes in mystuff.py
def apple():
print "I AM APPLES!"
~~~
當我寫完以上代碼,我就可以通過import來調用`mystuff`模塊,并訪問`apple`函數:
~~~
import mystuff
mystuff.apple()
~~~
我還可以增加一個叫做`tangerine`的變量:
~~~
def apple():
print "I AM APPLES!"
# this is just a variable
tangerine = "Living reflection of a dream"
~~~
可以用相同的方法來訪問:
~~~
import mystuff
mystuff.apple()
print mystuff.tangerine
~~~
返回再看字典,你應該發現模塊的使用跟字典是相似的,只不過語法不同,我們比較一下:
~~~
mystuff['apple'] # get apple from dict
mystuff.apple() # get apple from the module
mystuff.tangerine # same thing, it's just a variable
~~~
也就是說我們在Python中有一套通用的模式:
> 1. 有一個key=value模式的容器
> 1. 通過key從容器中獲取數據
在字典中,key是一個字符串,寫法為`[key]`,在模塊中,key是一個標識符,寫法為`.key`,在其余的地方,他們幾乎是一樣的。
## 類就像模塊
你可以認為一個模塊就是一個可以存放Python代碼的特殊字典,這樣你就可以通過`.`操作符訪問它。Python還有一個另外的結構提供相似的功能,就是類(class)。類的作用是組織一系列的函數和數據并將它們放在一個容器里,這樣你可以通過`.`操作符訪問到它們。
如果我想創建一個類似`mystuff`的類,我需要這樣做:
~~~
class MyStuff(object):
def __init__(self):
self.tangerine = "And now a thousand years between"
def apple(self):
print "I AM CLASSY APPLES!"
~~~
這相比于模塊復雜一些,對比之下肯定會有不同,但是你應該能返現這個類就像寫一個包含`apple()`方法的“迷你”`mystuff`模塊一樣.讓你困惑的地方可能是`__init__()`這個方法以及使用`self.tangerine`給`tangerine`賦值。
這正是為什么要使用類而不是僅有模塊的原因:你可以使用`Mystuff`這個類,還可以用它來創建更多個`Mystuff`,而他們之間也不會互相沖突。當你導入一個模塊時,你的整個項目也就只有一個這個模塊。
在你理解這些之前,你需要明白什么是“對象”,以及如何像使用`Mystuff.py`模塊一樣使用`Mystuff`這個類。
## 對象就像導入
如果一個類就像一個迷你模塊,那么類也會有一個類似`import`的概念,這個概念被稱為實例化,這只是對創建一種更虛幻更聰明的叫法。當一個類被實例化,你就得到一個類的對象。
實例化類的方法就是像調用函數一樣調用這個類:
~~~
thing = MyStuff()
thing.apple()
print thing.tangerine
~~~
第一行就是實例化的操作,這個操作多像是在調用一個函數啊。當然了,python在幕后幫你做了一系列的事情,我帶你來看看具體的調用步驟:
> 1. python 查找 `MyStuff()` 并確認它是你已經定義過的類
> 1. python創建一個空的對象,該對象擁有你在類中用`def`創建的所有函數
> 1. python看你是否創建了`__init__`函數,如果有,調用該方法初始化你新創建的空對象
> 1. 在`MyStuff`中,`__init__`方法有一個額外的變量`self`,這是python為我創建的一個空的對象,我可以在其上設置變量。
> 1. 然后,我給`self.tangerine`設置一首歌詞,然后初始化這個對象
> 1. python可以使用這個新創建好的對象,并將其分配給我可以使用的變量`thing`。
這就是當你調用一個類的時候,python做的事情。記住,這并不是把類給你,而是把類作為藍本來創建這種類型東西的副本。
我給了你一個類的簡單工作原理,這樣你就可以基于你所了解的模塊,建立你對類的理解。事實上,在這一點上,類和對象與模塊是有區別的:
> - 類是用來創建迷你模塊的藍本或定義
> - 實例化是如何創建這些小模塊,并在同一時間將其導入。實例化僅僅是指通過類創建一個對象。
> - 由此產生的迷你模塊被稱為對象,你可以將其分配給一個變量,讓它開始運行
在這一點上,對象和模塊的表現不同,這只是提供一種讓你理解類和對象的方法。
## 從一個事物中獲取事物
現在我提供給你3中方法從一個事物中獲取另一個:
~~~
# dict style
mystuff['apples']
# module style
mystuff.apples()
print mystuff.tangerine
# class style
thing = MyStuff()
thing.apples()
print thing.tangerine
~~~
## 類的舉例
你應該可以看到在這三個key=value的容器類型有一定的相似性,你也可能有一堆問題.記住這些問題,下一節練習會訓練你關于“面向對象的詞匯”。這節練習中,我只想讓你輸入這些代碼并保證它能正常運行,這樣能讓你再繼續下一節練習之前獲得一些經驗。
~~~
class Song(object):
def __init__(self, lyrics):
self.lyrics = lyrics
def sing_me_a_song(self):
for line in self.lyrics:
print line
happy_bday = Song(["Happy birthday to you",
"I don't want to get sued",
"So I'll stop right there"])
bulls_on_parade = Song(["They rally around tha family",
"With pockets full of shells"])
happy_bday.sing_me_a_song()
bulls_on_parade.sing_me_a_song()
~~~
## 你應該看到的內容
~~~
$ python ex40.py
Happy birthday to you
I don't want to get sued
So I'll stop right there
They rally around tha family
With pockets full of shells
~~~
## 附加題
> 1. 用本節學到的內容多寫幾首歌,確定你明白你把一個字符串的列表作為歌詞傳遞進去。
> 1. 把歌詞放進一個單獨的變量,再把這個變量傳遞給類
> 1. 看看你能不能讓這個類做更多的事情,如果沒什么想法也沒關系,試試看,會有什么
> 1. 在網上搜索一些“面向對象編程”的資料,嘗試讓你的大腦填滿這些資料。如果這些資料對你沒有用,也沒關系,這些東西有一半對我來說也是沒有意義的。
## 常見問題
### Q:為什么我在類里創建`__init__`或其他函數的時候需要使用`self`?
> 如果你不實用`self`,像這種代碼`cheese = 'Frank'`就是不明確的. 代碼并不清楚你指的是實例的`cheese`屬性,還是一個叫做`cheese`d的全局變量。如果你使用`self.cheese='Frank'`代碼就會十分清楚你指的就是實例中的屬性`self.cheese`.
- 序言
- 前言
- 簡介
- 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)
- 下一步