# 習題 41: 來自 Percal 25 號行星的哥頓人(Gothons)
你在上一節中發現 dict 的秘密功能了嗎?你可以解釋給自己嗎?讓我來給你解釋一下,順便和你自己的理解對比看有什么不同。這里是我們要討論的代碼:
~~~
cities['_find'] = find_city
city_found = cities['_find'](cities, state)
~~~
你要記住一個函數也可以作為一個變量,``def find_city`` 比如這一句創建了一個你可以在任何地方都能使用的變量。在這段代碼里,我們首先把函數 find_city 放到叫做 cities 的字典中,并將其標記為 '_find'。 這和我們將州和市關聯起來的代碼做的事情一樣,只不過我們在這里放了一個函數的名稱。
好了,所以一旦我們知道 find_city 是在字典中 _find 的位置,這就意味著我們可以去調用它。第二行代碼可以分解成如下步驟:
1. Python 看到 city_found= 于是知道了需要創建一個變量。
1. 然后它讀到 cities ,然后知道了它是一個字典
1. 然后看到了 ['_find'] ,于是 Python 就從索引找到了字典 cities 中對應的位置,并且獲取了該位置的內容。
1. ['_find'] 這個位置的內容是我們的函數 find_city ,所以 Python 就知道了這里表示一個函數,于是當它碰到 ( 就開始了函數調用。
1. cities,state 這兩個參數將被傳遞到函數 find_city 中,然后這個函數就被運行了。
1. find_city 接著從 cities 中尋找 states ,并且返回它找到的內容,如果什么都沒找到,就返回一個信息說它什么都沒找到。
1. Python find_city 接受返回的信息,最后將該信息賦值給一開始的 city_found 這個變量。
我再教你一個小技巧。如果你倒著閱讀的話,代碼可能會變得更容易理解。讓我們來試一下,一樣是那行:
1. state 和 city 是...
1. 作為參數傳遞給...
1. 一個函數,位置在...
1. '_find' 然后尋找,目的地為...
1. cities 這個位置...
1. 最后賦值給 city_found.
還有一種方法讀它,這回是“由里向外”。
1. 找到表達式的中心位置,此次為 ['_find'].
1. 逆時針追溯,首先看到的是一個叫 cities 的字典,這樣就知道了 cities 中的 _find 元素。
1. 上一步得到一個函數。繼續逆時針尋找,看到的是參數。
1. 參數傳遞給函數后,函數會返回一個值。然后再逆時針尋找。
1. 最后,我們到了 city_found= 的賦值位置,并且得到了最終結果。
數十年的編程下來,我在讀代碼的過程中已經用不到上面的三種方法了。我只要瞟一眼就能知道它的意思。甚至給我一整頁的代碼,我也可以一眼瞄出里邊的 bug 和錯誤。這樣的技能是花了超乎常人的時間和精力才鍛煉得來的。在磨練的過程中,我學會了下面三種讀代碼的方法,它們適用于幾乎所有的編程語言:
1. 從前向后。
1. 從后向前。
1. 逆時針方向。
下次碰到難懂的語句時,你可以試試這三種方法。
現在我們來寫這次的練習,寫完后再過一遍,這節習題其實挺有趣的。
<table class="highlighttable"><tbody><tr><td class="linenos"> <div class="linenodiv"> <pre> 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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81</pre> </div> </td> <td class="code"> <div class="highlight"> <pre>import random
from urllib import urlopen
import sys
WORD_URL = "http://learncodethehardway.org/words.txt"
WORDS = []
PHRASES = {
"class ###(###):":
"Make a class named ### that is-a ###.",
"class ###(object):\n\tdef __init__(self, ***)" :
"class ### has-a __init__ that takes self and *** parameters.",
"class ###(object):\n\tdef ***(self, @@@)":
"class ### has-a function named *** that takes self and @@@ parameters.",
"*** = ###()":
"Set *** to an instance of class ###.",
"***.***(@@@)":
"From *** get the *** function, and call it with parameters self, @@@.",
"***.*** = '***'":
"From *** get the *** attribute and set it to '***'."
}
# do they want to drill phrases first
PHRASE_FIRST = False
if len(sys.argv) == 2 and sys.argv[1] == "english":
PHRASE_FIRST = True
# load up the words from the website
for word in urlopen(WORD_URL).readlines():
WORDS.append(word.strip())
def convert(snippet, phrase):
class_names = [w.capitalize() for w in
random.sample(WORDS, snippet.count("###"))]
other_names = random.sample(WORDS, snippet.count("***"))
results = []
param_names = []
for i in range(0, snippet.count("@@@")):
param_count = random.randint(1,3)
param_names.append(', '.join(random.sample(WORDS, param_count)))
for sentence in snippet, phrase:
result = sentence[:]
# fake class names
for word in class_names:
result = result.replace("###", word, 1)
# fake other names
for word in other_names:
result = result.replace("***", word, 1)
# fake parameter lists
for word in param_names:
result = result.replace("@@@", word, 1)
results.append(result)
return results
# keep going until they hit CTRL-D
try:
while True:
snippets = PHRASES.keys()
random.shuffle(snippets)
for snippet in snippets:
phrase = PHRASES[snippet]
question, answer = convert(snippet, phrase)
if PHRASE_FIRST:
question, answer = answer, question
print question
raw_input("> ")
print "ANSWER: %s\n\n" % answer
except EOFError:
print "\nBye"
</pre> </div> </td> </tr></tbody></table>
代碼不少,不過還是從頭寫完吧。確認它能運行,然后玩一下看看。
### 你應該看到的結果
我玩起來時這樣的:
~~~
$ python ex41.py
bat.bait(children)
> From bat get the bait function and call it with self and children arguments.
ANSWER: From bat get the bait function, and call it with parameters self, children.
class Brake(object):
def __init__(self, beef)
> class Brake has a __init__ function that takes self and beef parameters.
ANSWER: class Brake has-a __init__ that takes self and beef parameters.
class Cow(object):
def crook(self, cushion)
> class Cow has-a function named crook that takes self and cushion params.
ANSWER: class Cow has-a function named crook that takes self and cushion parameters.
cast = Beetle()
> Set cast to an instance of class Beetle.
ANSWER: Set cast to an instance of class Beetle.
cent.coach = 'appliance'
> From cent get the coach attribute and set it to appliance.
ANSWER: From cent get the coach attribute and set it to 'appliance'.
class Destruction(Committee):
> ^D
Bye
~~~
### 加分習題
1. 解釋一下返回至下一個房間的工作原理。
1. 創建更多的房間,讓游戲規模變大。
1. 除了讓每個函數打印自己以外,再學習一下“文檔字符串(doc strings)”式的注解。看看你能不能將房間描述寫成文檔注解,然后修改運行它的代碼,讓它把文檔注解打印出來。
1. 一旦你用了文檔注解作為房間描述,你還需要讓這個函數打印出用戶提示嗎?試著讓運行函數的代碼打出用戶提示來,然后將用戶輸入傳遞到各個函數。你的函數應該只是一些 if 語句組合,將結果打印出來,并且返回下一個房間。
1. 這其實是一個小版本的“有限狀態機(finite state machine)”,找資料閱讀了解一下,雖然你可能看不懂,但還是找來看看吧。
1. 我的代碼里有一個 bug,為什么門鎖要猜測 11 次?
- 譯者前言
- 前言:笨辦法更簡單
- 習題 0: 準備工作
- 習題 1: 第一個程序
- 習題 2: 注釋和井號
- 習題 3: 數字和數學計算
- 習題 4: 變量(variable)和命名
- 習題 5: 更多的變量和打印
- 習題 6: 字符串(string)和文本
- 習題 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: 來自 Percal 25 號行星的哥頓人(Gothons)
- 習題 42: 物以類聚
- 習題 43: 你來制作一個游戲
- 習題 44: 給你的游戲打分
- 習題 45: 對象、類、以及從屬關系
- 習題 46: 一個項目骨架
- 習題 47: 自動化測試
- 習題 48: 更復雜的用戶輸入
- 習題 49: 創建句子
- 習題 50: 你的第一個網站
- 習題 51: 從瀏覽器中獲取輸入
- 習題 52: 創建你的 web 游戲
- 下一步
- 老程序員的建議