💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 习题 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&#13; 2&#13; 3&#13; 4&#13; 5&#13; 6&#13; 7&#13; 8&#13; 9&#13; 10&#13; 11&#13; 12&#13; 13&#13; 14&#13; 15&#13; 16&#13; 17&#13; 18&#13; 19&#13; 20&#13; 21&#13; 22&#13; 23&#13; 24&#13; 25&#13; 26&#13; 27&#13; 28&#13; 29&#13; 30&#13; 31&#13; 32&#13; 33&#13; 34&#13; 35&#13; 36&#13; 37&#13; 38&#13; 39&#13; 40&#13; 41&#13; 42&#13; 43&#13; 44&#13; 45&#13; 46&#13; 47&#13; 48&#13; 49&#13; 50&#13; 51&#13; 52&#13; 53&#13; 54&#13; 55&#13; 56&#13; 57&#13; 58&#13; 59&#13; 60&#13; 61&#13; 62&#13; 63&#13; 64&#13; 65&#13; 66&#13; 67&#13; 68&#13; 69&#13; 70&#13; 71&#13; 72&#13; 73&#13; 74&#13; 75&#13; 76&#13; 77&#13; 78&#13; 79&#13; 80&#13; 81</pre> </div> </td> <td class="code"> <div class="highlight"> <pre>import random&#13; from urllib import urlopen&#13; import sys&#13; &#13; WORD_URL = "http://learncodethehardway.org/words.txt"&#13; WORDS = []&#13; &#13; PHRASES = {&#13; "class ###(###):":&#13; "Make a class named ### that is-a ###.",&#13; "class ###(object):\n\tdef __init__(self, ***)" :&#13; "class ### has-a __init__ that takes self and *** parameters.",&#13; "class ###(object):\n\tdef ***(self, @@@)":&#13; "class ### has-a function named *** that takes self and @@@ parameters.",&#13; "*** = ###()":&#13; "Set *** to an instance of class ###.",&#13; "***.***(@@@)":&#13; "From *** get the *** function, and call it with parameters self, @@@.",&#13; "***.*** = '***'":&#13; "From *** get the *** attribute and set it to '***'."&#13; }&#13; &#13; # do they want to drill phrases first&#13; PHRASE_FIRST = False&#13; if len(sys.argv) == 2 and sys.argv[1] == "english":&#13; PHRASE_FIRST = True&#13; &#13; # load up the words from the website&#13; for word in urlopen(WORD_URL).readlines():&#13; WORDS.append(word.strip())&#13; &#13; &#13; def convert(snippet, phrase):&#13; class_names = [w.capitalize() for w in&#13; random.sample(WORDS, snippet.count("###"))]&#13; other_names = random.sample(WORDS, snippet.count("***"))&#13; results = []&#13; param_names = []&#13; &#13; for i in range(0, snippet.count("@@@")):&#13; param_count = random.randint(1,3)&#13; param_names.append(', '.join(random.sample(WORDS, param_count)))&#13; &#13; for sentence in snippet, phrase:&#13; result = sentence[:]&#13; &#13; # fake class names&#13; for word in class_names:&#13; result = result.replace("###", word, 1)&#13; &#13; # fake other names&#13; for word in other_names:&#13; result = result.replace("***", word, 1)&#13; &#13; # fake parameter lists&#13; for word in param_names:&#13; result = result.replace("@@@", word, 1)&#13; &#13; results.append(result)&#13; &#13; return results&#13; &#13; &#13; # keep going until they hit CTRL-D&#13; try:&#13; while True:&#13; snippets = PHRASES.keys()&#13; random.shuffle(snippets)&#13; &#13; for snippet in snippets:&#13; phrase = PHRASES[snippet]&#13; question, answer = convert(snippet, phrase)&#13; if PHRASE_FIRST:&#13; question, answer = answer, question&#13; &#13; print question&#13; &#13; raw_input("&gt; ")&#13; print "ANSWER: %s\n\n" % answer&#13; except EOFError:&#13; print "\nBye"&#13; </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 次?