原文:Learn Python the Hard Way, 5th Edition (Early Release)
译者:飞龙
协议:CC BY-NC-SA 4.0
练习 30:假如
这是你将要输入的下一个 Python 脚本,它向你介绍了if
语句。输入这个代码,确保它能够完美运行,然后我们将看看你的练习是否有所收获。
列表 30.1:ex30.py
1 people = 20
2 cats = 30
3 dogs = 15
4
5
6 if people < cats:
7 print("Too many cats! The world is doomed!")
8
9 if people > cats:
10 print("Not many cats! The world is saved!")
11
12 if people < dogs:
13 print("The world is drooled on!")
14
15 if people > dogs:
16 print("The world is dry!")
17
18
19 dogs += 5
20
21 if people >= dogs:
22 print("People are greater than or equal to dogs.")
23
24 if people <= dogs:
25 print("People are less than or equal to dogs.")
26
27
28 if people == dogs:
29 print("People are dogs.")
你应该看到的内容
1 Too many cats! The world is doomed!
2 The world is dry!
3 People are greater than or equal to dogs.
4 People are less than or equal to dogs.
5 People are dogs.
dis()
它
在接下来的几个练习中,我希望你运行dis()
在你正在学习的一些代码上,以便更深入地了解它是如何工作的:
1 from dis import dis
2
3 dis('''
4 if people < cats:
5 print("Too many cats! The world is doomed!")
6 ''')
这不是你在编程时通常会做的事情。我只是希望你在这里这样做,以便为你理解正在发生的事情提供另一种可能的方式。如果dis()
并没有真正帮助你更好地理解代码,那么随意这样做并忘记它。
要研究这个问题,只需将 Python 代码放在这个dis()
输出旁边,然后尝试识别与字节码匹配的 Python 代码行。
练习题
在这个练习中,试着猜测if
语句是什么以及它的作用是什么。在继续下一个练习之前,尝试用自己的话回答这些问题:
-
你认为
if
对下面的代码有什么影响? -
为什么
if
下面的代码需要缩进四个空格? -
如果没有缩进会发生什么?
-
你能否在
if
语句中放入来自练习 28 的其他布尔表达式?试一试。 -
如果你改变
people
、cats
和dogs
的初始值会发生什么?
常见学生问题
+=
是什么意思? 代码x += 1
与x = x + 1
相同,但输入更少。你可以称之为“递增运算符”。对于-=
和许多其他表达式,你以后会学到的也是一样。
练习 31:否则和如果
在上一个练习中,你解决了一些if 语句
,然后试图猜测它们是什么以及它们如何工作。在学习更多之前,我将通过回答你在学习练习中提出的问题来解释一切。你做了学习练习,对吧?
-
你认为
if
对其下面的代码有什么影响?if 语句
在代码中创建了所谓的“分支”。这有点像那些选择你自己冒险的书,如果你做出一个选择,就会被要求翻到一页,如果你选择另一条路,就会翻到另一页。if 语句
告诉你的脚本,“如果这个布尔表达式为真,则运行其下的代码;否则跳过它。” -
为什么
if
下面的代码需要缩进四个空格?在一行的末尾加上冒号是告诉 Python 你将创建一个新的代码“块”,然后缩进四个空格告诉 Python 哪些代码行在该块中。这与你在本书的前半部分创建函数时所做的事情完全相同。 -
如果没有缩进会发生什么?如果没有缩进,你很可能会产生 Python 错误。Python 希望你在以
:
(冒号)结尾的行之后缩进一些东西。 -
你能把练习 28 中的其他布尔表达式放在
if 语句
中吗?试试看。是的,你可以,而且它们可以尽可能复杂,尽管非常复杂的东西通常是不好的风格。 -
如果更改
people
,cats
和dogs
的初始值会发生什么?因为你正在比较数字,如果更改数字,不同的if 语句
将评估为True
,并且其下的代码块将运行。回去放入不同的数字,看看你是否能在脑海中弄清楚哪些代码块将运行。
将我的答案与你的答案进行比较,并确保你真正理解代码“块”的概念。这对于你做下一个练习很重要,其中你将编写所有可以使用的if 语句
的部分。
将这个输入并使其工作。
列表 31.1:ex31.py
1 people = 30
2 cars = 40
3 trucks = 15
4
5
6 if cars > people:
7 print("We should take the cars.")
8 elif cars < people:
9 print("We should not take the cars.")
10 else:
11 print("We can't decide.")
12
13 if trucks > cars:
14 print("That's too many trucks.")
15 elif trucks < cars:
16 print("Maybe we could take the trucks.")
17 else:
18 print("We still can't decide.")
19
20 if people > trucks:
21 print("Alright, let's just take the trucks.")
22 else:
23 print("Fine, let's stay home then.")
你应该看到什么
1 We should take the cars.
2 Maybe we could take the trucks.
3 Alright, let's just take the trucks.
dis()
它
我们现在到了一个dis()
有点太复杂的地步。让我们只选择一个代码块来学习:
1 from dis import dis
2
3 dis('''
4 if cars > people:
5 print("We should take the cars.")
6 elif cars < people:
7 print("We should not take the cars.")
8 else:
9 print("We can't decide.")
10 ''')
我认为学习这个最好的方法是将 Python 代码放在dis()
输出旁边,尝试将 Python 代码的行与其字节码匹配。如果你能做到这一点,那么你将远远领先于许多甚至不知道 Python 有dis()
的 Python 程序员。
如果你搞不清楚,不要担心。这一切都是关于尽可能推动你的知识,以找到理解 Python 的新方法。
学习练习
-
试着猜猜
elif
和else
在做什么。 -
更改
cars
,people
和trucks
的数字,然后跟踪每个if 语句
,看看将打印出什么。 -
尝试一些更复杂的布尔表达式,比如
cars > people or trucks < cars
。 -
在每行上方写出该行的英文描述。
常见学生问题
如果多个 elif
块都为 True
会发生什么? Python 从顶部开始运行第一个为True
的块,因此只会运行第一个。
练习 32:做决策
在这本书的前半部分,你主要只是打印出一些称为“函数”的东西,但一切基本上都是直线的。你的脚本从顶部开始运行,一直到底部结束。如果你创建了一个函数,你可以稍后运行该函数,但它仍然没有你真正需要做出决策的分支。现在你有了 if
、else
和 elif
,你可以开始编写决策性的脚本了。
在上一个脚本中,你列出了一组简单的测试,询问一些问题。在这个脚本中,你将询问用户问题,并根据他们的答案做出决定。编写这个脚本,然后多玩一下,弄清楚它的运行方式。
代码清单 32.1: ex32.py
1 print("""You enter a dark room with two doors.
2 Do you go through door #1 or door #2?""")
3
4 door = input("> ")
5
6 if door == "1":
7 print("There's a giant bear here eating a cheese cake.")
8 print("What do you do?")
9 print("1\. Take the cake.")
10 print("2\. Scream at the bear.")
11
12 bear = input("> ")
13
14 if bear == "1":
15 print("The bear eats your face off. Good job!")
16 elif bear == "2":
17 print("The bear eats your legs off. Good job!")
18 else:
19 print(f"Well, doing {bear} is probably better.")
20 print("Bear runs away.")
21
22 elif door == "2":
23 print("You stare into the endless abyss at Cthulhu's retina.")
24 print("1\. Blueberries.")
25 print("2\. Yellow jacket clothespins.")
26 print("3\. Understanding revolvers yelling melodies.")
27
28 insanity = input("> ")
29
30 if insanity == "1" or insanity == "2":
31 print("Your body survives powered by a mind of jello.")
32 print("Good job!")
33 else:
34 print("The insanity rots your eyes into a pool of muck.")
35 print("Good job!")
36
37 else:
38 print("You stumble around and fall on a knife and die. Good job!")
这里的关键点是,现在你正在将if-statements
放在if-statements
内部作为可以运行的代码。这是非常强大的,可以用来创建“嵌套”决策,其中一个分支导致另一个分支。
确保你理解了if
-statements 中嵌套if
-statements 的概念。实际上,做一些练习来真正掌握它。
你应该看到的结果
这是我玩这个小冒险游戏的情况。我表现得不太好。
1 You enter a dark room with two doors.
2 Do you go through door #1 or door #2?
3 > 1
4 There's a giant bear here eating a cheese cake.
5 What do you do?
6 1\. Take the cake.
7 2\. Scream at the bear.
8 > 2
9 The bear eats your legs off. Good job!
dis()
它
这次没有 *dis()*
It 部分,因为这段代码太复杂了,难以理解,但如果你感觉幸运的话,可以尝试一下:
1 from dis import dis
2
3 if door == "1":
4 print("1")
5 bear = input("> ")
6 if bear == "1":
7 print("bear 1")
8 elif bear == "2":
9 print("bear 2")
10 else:
11 print("bear 3")
这将产生大量需要分析的代码,但尽力而为。过一段时间会变得无聊,但也有助于理解 Python 的工作原理。再次强调,如果这让你困惑,可以先跳过,以后再尝试。
练习
-
制作游戏的新部分,并改变人们可以做出的决定。在游戏变得荒谬之前尽可能扩展游戏。
-
编写一个全新的游戏。也许你不喜欢这个,那就自己创造一个。这是你的电脑;做你想做的事情。
常见学生问题
你能用一系列 if-else
组合替换 elif
吗? 在某些情况下可以,但这取决于每个 if/else
的编写方式。这也意味着 Python 将检查每个 if-else
组合,而不像 if-elif-else
那样只检查第一个为假的条件。尝试创建一些来了解差异。
如何判断一个数字是否在一系列数字范围内? 你有两个选择:使用 0 < x
< 10
或 1 <= x < 10
—这是经典的表示法—或使用 x in range(1, 10)
。
如果我想在 if-elif-else
块中增加更多选项怎么办? 为每个可能的选择添加更多 elif
块。
练习 33:循环和列表
现在你应该能够编写一些更有趣的程序了。如果你一直在跟进,你应该意识到现在你可以将所有其他学到的东西与if-statements
和布尔表达式结合起来,使你的程序做一些聪明的事情。
然而,程序也需要快速地执行重复的事情。在这个练习中,我们将使用for-loop
来构建和打印各种列表。当你做这个练习时,你会开始明白它们是什么。我现在不会告诉你。你必须自己弄清楚。
在使用for-loop
之前,你需要一种方法来存储循环的结果。最好的方法是使用lists
。Lists
正是它们的名字所说的:一个按照从头到尾顺序组织的东西的容器。这并不复杂;你只需要学习一种新的语法。首先,这是如何创建lists
的:
1 hairs = ['brown', 'blond', 'red']
2 eyes = ['brown', 'blue', 'green']
3 weights = [1, 2, 3, 4]
你用[
(左括号)开始list
,这个“打开”list
。然后你用逗号分隔每个你想要放入列表的项目,类似于函数参数。最后,用]
(右括号)结束列表以表示它的结束。然后 Python 将获取这个列表及其所有内容,并将它们分配给变量。
警告!
这就是对于不能编码的人来说变得棘手的地方。你的大脑被教导世界是平的。还记得在上一个练习中你是如何在if-statements
内部放置if-statements
的吗?那可能让你的大脑感到疼痛,因为大多数人不会考虑如何在“嵌套”事物内部放置事物。在编程中,嵌套结构随处可见。你会发现调用其他函数的函数,这些函数有带有列表的if-statements
,列表内部还有列表。如果你看到这样的结构而无法理解,拿出一支铅笔和纸,逐步手动分解,直到你理解为止。
现在我们将使用一些for-loops
来构建一些列表并将它们打印出来:
列表 33.1:ex33.py
1 the_count = [1, 2, 3, 4, 5]
2 fruits = ['apples', 'oranges', 'pears', 'apricots']
3 change = [1, 'pennies', 2, 'dimes', 3, 'quarters']
4
5 # this first kind of for-loop goes through a list
6 for number in the_count:
7 print(f"This is count {number}")
8
9 # same as above
10 for fruit in fruits:
11 print(f"A fruit of type: {fruit}")
12
13 # also we can go through mixed lists too
14 for i in change:
15 print(f"I got {i}")
16
17 # we can also build lists, first start with an empty one
18 elements = []
19
20 # then use the range function to do 0 to 5 counts
21 for i in range(0, 6):
22 print(f"Adding {i} to the list.")
23 # append is a function that lists understand
24 elements.append(i)
25
26 # now we can print them out too
27 for i in elements:
28 print(f"Element was: {i}")
你应该看到的内容
1 This is count 1
2 This is count 2
3 This is count 3
4 This is count 4
5 This is count 5
6 A fruit of type: apples
7 A fruit of type: oranges
8 A fruit of type: pears
9 A fruit of type: apricots
10 I got 1
11 I got pennies
12 I got 2
13 I got dimes
14 I got 3
15 I got quarters
16 Adding 0 to the list.
17 Adding 1 to the list.
18 Adding 2 to the list.
19 Adding 3 to the list.
20 Adding 4 to the list.
21 Adding 5 to the list.
22 Element was: 0
23 Element was: 1
24 Element was: 2
25 Element was: 3
26 Element was: 4
27 Element was: 5
dis()
它
这次让我们简单点,只看看 Python 如何执行for-loop
:
1 from dis import dis
2
3 dis('''
4 for number in the_count:
5 print(number)
6 ''')
这次我将在这里重现输出,以便我们可以分析它:
1 0 LOAD_NAME 0 (the_count) # get the count list
2 2 GET_ITER # start iteration
3 4 FOR_ITER 6 (to 18) # for-loop jump to 18
4 6 STORE_NAME 1 (number) # create number variable
5
6 8 LOAD_NAME 2 (print) # load print()
7 10 LOAD_NAME 1 (number) # load number
8 12 CALL_FUNCTION 1 # call print()
9 14 POP_TOP # clean stack
10 16 JUMP_ABSOLUTE 2 (to 4) # jump back to FOR_ITER at 4
11
12 18 LOAD_CONST 0 (None) # jump here when FOR_ITER done
13 20 RETURN_VALUE
在FOR_ITER
操作中我们看到了一个新的东西。这个操作通过以下步骤使for-loop
工作:
-
调用
the_count.__next__()
-
如果
the_count
中没有更多元素,则跳转到 18 -
如果仍然有元素,则继续执行
-
STORE_NAME
然后将the_count.__next__()
的结果赋给名为number
的变量
这就是for-loop
实际上所做的一切。它主要是一个单字节代码FOR_ITER
,结合其他几个来遍历列表。
学习练习
-
看看你如何使用了
range
。查阅range
函数以了解它。 -
在第 22 行完全避免了那个
for-loop
,直接将range(0,6)
赋给elements
,你能做到吗? -
查找关于列表的 Python 文档并阅读它们。除了
append
之外,你还可以对列表进行哪些操作?
常见学生问题
如何创建二维(2D)列表? 就像这样的列表中嵌套列表:[[1,2,3],[4,5,6]]
列表和数组不是一回事吗? 这取决于语言和实现。在传统术语中,列表与数组非常不同,因为它们的实现方式不同。在 Ruby 中,它们称之为“数组”。在 Python 中,它们称之为“列表”。现在只需称之为“列表”,因为这是 Python 的称呼。
为什么 for 循环能够使用尚未定义的变量? 变量在循环开始时由 for 循环
定义,每次迭代时将其初始化为当前循环元素。
为什么 for i in range(1, 3):
只循环两次而不是三次? range()
函数只生成从第一个到最后一个的数字,不包括最后一个。因此,在上述情况下它在两处停止,而不是三处。这实际上是这种循环最常见的方式。
elements.append()
做什么?它简单地将元素附加到列表的末尾。打开 Python shell 并尝试用自己创建的列表做几个示例。每当遇到这样的情况时,总是尝试在 Python shell 中进行交互操作。
练习 34:While 循环
现在让我们用一个新的循环完全震惊你,while-loop
。while-loop
会持续执行其下的代码块,只要布尔表达式为True
。
等等,你一直跟上术语了吗?如果我们写一行并以:
(冒号)结尾,那告诉 Python 开始一个新的代码块?然后我们缩进,这就是新代码。这一切都是关于构建你的程序,让 Python 知道你的意图。如果你没有理解这个概念,那就回去多做一些关于if
语句、函数和for
循环的工作,直到你理解为止。
后面我们会有一些练习,训练你的大脑阅读这些结构,类似于我们如何将布尔表达式烙印在你的大脑中。
回到while-loop
。它们的作用就像一个if
语句的测试,但不同于只运行代码块一次,它们会跳回到while
所在的“顶部”,并重复。while
循环会一直运行,直到表达式为False
。
while
循环的问题在于:有时它们不会停止。如果你的意图只是一直循环直到宇宙的尽头,那么这很好。否则,你几乎总是希望你的循环最终会结束。
为了避免这些问题,有一些规则需要遵循:
-
确保你谨慎使用
while
循环。通常for
循环更好。 -
检查你的
while
语句,并确保布尔测试最终会变为False
。 -
如果有疑问,在
while
循环的顶部和底部打印出你的测试变量,看看它在做什么。
在这个练习中,你将学习while
循环,并在进行以下三个检查时使用它们:
列表 34.1: ex34.py
1 i = 0
2 numbers = []
3
4 while i < 6:
5 print(f"At the top i is {i}")
6 numbers.append(i)
7
8 i = i + 1
9 print("Numbers now: ", numbers)
10 print(f"At the bottom i is {i}")
11
12
13 print("The numbers: ")
14
15 for num in numbers:
16 print(num)
你应该看到的结果
1 At the top i is 0
2 Numbers now: [0]
3 At the bottom i is 1
4 At the top i is 1
5 Numbers now: [0, 1]
6 At the bottom i is 2
7 At the top i is 2
8 Numbers now: [0, 1, 2]
9 At the bottom i is 3
10 At the top i is 3
11 Numbers now: [0, 1, 2, 3]
12 At the bottom i is 4
13 At the top i is 4
14 Numbers now: [0, 1, 2, 3, 4]
15 At the bottom i is 5
16 At the top i is 5
17 Numbers now: [0, 1, 2, 3, 4, 5]
18 At the bottom i is 6
19 The numbers:
20 0
21 1
22 2
23 3
24 4
25 5
dis()
它
在我们代码之游戏的最终“支线任务”中,你将使用dis()
来分析while-loop
的工作原理:
1 from dis import dis
2
3 dis('''
4 i = 0
5 while i < 6:
6 i = i + 1
7 ''')
你已经看到了大部分这些字节码,所以现在轮到你去弄清楚这个dis()
输出与 Python 有什么关系了。记住你可以在文档的末尾的[dis()](https://docs.python.org/3/library/dis.xhtml#python-bytecode-instructions)
文档中查找所有的字节码。祝你好运!
学习练习
-
将这个
while-loop
转换为一个可以调用的函数,并用一个变量替换测试中的6
(i < 6
)。 -
使用这个函数来重写脚本以尝试不同的数字。
-
在函数参数中添加另一个变量,你可以传入它,以便你可以更改第 8 行的
+ 1
,这样你就可以改变增量是多少。 -
再次重写脚本以使用这个函数,看看会有什么影响。
-
重写它以使用
for-loops
和range
。你还需要在中间保留增量器吗?如果不去掉它会发生什么?
如果在任何时候你这样做时出现问题(很可能会),只需按住CTRL
并按下c
(CTRL-c
),程序就会中止。
常见学生问题
for
-循环和**while
-循环有什么区别?for
-循环只能在“集合”上进行迭代(循环)。while
-循环可以进行任何类型的迭代(循环)。然而,while
-循环更难正确使用,通常可以用for
**-循环完成许多任务。
循环很难。我该如何理解它们? 人们不理解循环的主要原因是因为他们无法跟随代码的“跳跃”。当循环运行时,它会执行其代码块,最后跳回顶部。为了可视化这一点,在循环中到处放置print
语句,打印出 Python 在循环中运行的位置以及这些点上变量的设置。在循环之前、顶部、中间和底部编写print
行。研究输出并尝试理解正在进行的跳跃。
练习 35:分支和函数
你已经学会了if 语句
、函数和列表。现在是时候挑战你的思维了。把这个输入进去,看看你能否弄清楚它在做什么:
列表 35.1: ex35.py
1 from sys import exit
2
3 def gold_room():
4 print("This room is full of gold. How much do you take?")
5
6 choice = input("> ")
7 if "0" in choice or "1" in choice:
8 how_much = int(choice)
9 else:
10 dead("Man, learn to type a number.")
11
12 if how_much < 50:
13 print("Nice, you're not greedy, you win!")
14 exit(0)
15 else:
16 dead("You greedy bastard!")
17
18
19 def bear_room():
20 print("There is a bear here.")
21 print("The bear has a bunch of honey.")
22 print("The fat bear is in front of another door.")
23 print("How are you going to move the bear?")
24 bear_moved = False
25
26 while True:
27 choice = input("> ")
28
29 if choice == "take honey":
30 dead("The bear looks at you then slaps your face off.")
31 elif choice == "taunt bear" and not bear_moved:
32 print("The bear has moved from the door.")
33 print("You can go through it now.")
34 bear_moved = True
35 elif choice == "taunt bear" and bear_moved:
36 dead("The bear gets pissed off and chews your leg off.")
37 elif choice == "open door" and bear_moved:
38 gold_room()
39 else:
40 print("I got no idea what that means.")
41
42
43 def cthulhu_room():
44 print("Here you see the great evil Cthulhu.")
45 print("He, it, whatever stares at you and you go insane.")
46 print("Do you flee for your life or eat your head?")
47
48 choice = input("> ")
49
50 if "flee" in choice:
51 start()
52 elif "head" in choice:
53 dead("Well that was tasty!")
54 else:
55 cthulhu_room()
56
57
58 def dead(why):
59 print(why, "Good job!")
60 exit(0)
61
62 def start():
63 print("You are in a dark room.")
64 print("There is a door to your right and left.")
65 print("Which one do you take?")
66
67 choice = input("> ")
68
69 if choice == "left":
70 bear_room()
71 elif choice == "right":
72 cthulhu_room()
73 else:
74 dead("You stumble around the room until you starve.")
75
76
77 start()
你应该看到什么
这是我玩游戏的样子:
1 You are in a dark room.
2 There is a door to your right and left.
3 Which one do you take?
4 > left
5 There is a bear here.
6 The bear has a bunch of honey.
7 The fat bear is in front of another door.
8 How are you going to move the bear?
9 > taunt bear
10 The bear has moved from the door.
11 You can go through it now.
12 > open door
13 This room is full of gold. How much do you take?
14 > 1000
15 You greedy bastard! Good job!
学习练习
-
绘制游戏地图以及你如何在其中流动。
-
修复所有错误,包括拼写错误。
-
为你不理解的函数写注释。
-
添加更多内容到游戏中。你能做些什么来简化和扩展它?
-
gold_room
有一种奇怪的方式让你输入一个数字。这种方式存在哪些错误?你能比我写的更好吗?看看int()
的工作原理会有提示。
常见学生问题
救命!这个程序怎么运行的!? 当你在理解一段代码时遇到困难时,只需在每一行上面写一个英文注释,解释该行的作用。保持你的评论简短并与代码相似。然后要么画出代码的工作原理,要么写一段描述它的段落。如果你这样做,你就会理解它。
为什么你写了 while True
? 这会造成一个无限循环。
exit(0)
的作用是什么? 在许多操作系统上,一个程序可以通过 exit(0)
中止,传入的数字将指示是否有错误。如果你使用 exit(1)
,那么就会有一个错误,但 exit(0)
将是一个良好的退出。它与正常的布尔逻辑相反(0==False
)的原因是你可以使用不同的数字来指示不同的错误结果。你可以使用 exit(100)
来表示不同的错误结果,而不同于 exit(2)
或 exit(1)
。
为什么 input()
有时写成 input('> ')
? input
的参数是一个字符串,它应该在获取用户输入之前打印作为提示。
练习 36:设计和调试
现在你已经了解了if
语句,我将给你一些关于for
循环和while
循环的规则,这将帮助你避免麻烦。我还会给你一些关于调试的提示,这样你就可以找出程序中的问题。最后,你将设计一个类似于上一个练习但有些不同的小游戏。
从想法到可运行的代码
有一个简单的过程任何人都可以遵循,将你的想法转化为代码。这不是唯一的过程,但对许多人来说效果很好。在你开发自己的个人过程之前,使用这个过程。
-
以你理解的任何形式将你的想法表达出来。你是作家吗?那就写一篇关于你的想法的文章。你是艺术家或设计师吗?那就画出用户界面。你喜欢图表吗?看看序列图,这是编程中最有用的图之一。
-
为你的代码创建一个文件。是的,信不信由你,这是一个重要的步骤,大多数人都会遇到困难。如果你想不出一个名字,就随便挑一个吧。
-
用简单的英语(或者你最容易理解的语言)写下你的想法的描述作为注释。
-
从顶部开始,将第一个注释转换为“伪代码”,这有点像 Python,但你不用在意语法。
-
将那个“伪代码”转换为真正的 Python 代码,并不断运行你的文件,直到这段代码实现了你的注释所说的。
-
重复这个过程,直到你将所有的注释转换为 Python 代码。
-
退一步,审查你的代码,然后删除它。你不必一直这样做,但如果你养成丢弃第一个版本的习惯,你将获得两个好处:
a. 你的第二个版本几乎总是比第一个版本好。
b. 你向自己确认这不仅仅是愚蠢的运气。你确实能编写代码。这有助于应对冒名顶替综合症和增强自信。
让我们用一个简单的问题“创建一个简单的华氏度到摄氏度转换器”来做一个例子。第一步,我会写出我对转换的了解:
C 等于 (F - 32 ) / 1.8。我应该询问用户输入 F,然后打印出 C。
一个非常基本的数学公式是理解问题的简单方法。第二步,我写下描述我的代码应该做什么的注释:
1 # ask the user for the F
2 # convert it to a float()
3 # C = (F - 32) / 1.8
4 # print C to the user
一旦我有了这个,我会用伪代码“填空”。我只会做第一行,这样你就可以完成这个:
1 # ask the user for the F
2 F = input(?)
3
4 # convert it to a float()
5 # C = (F - 32) / 1.8
6 # print C to the user
注意,我故意懒惰,没有正确地编写语法,这就是伪代码的要点。一旦我有了这个,就将其转换为正确的 Python 代码:
1 # ask the user for the F
2 F = input("C? ")
3
4 # convert it to a float()
5 # C = (F - 32) / 1.8
6 # print C to the user
运行它! 你应该不断地运行你的代码。如果你输入了超过几行,只需删除它们,重新开始。这样会容易得多。
现在这些行起作用了,我继续下一个注释并重复这个过程,直到我将所有的注释转换成 Python。当我的脚本最终工作时,我会删除它并使用我所知道的重新编写它。也许这一次我直接写 Python,或者我再次重复这个过程。这样做会让我确认自己实际上是可以做到的。这不仅仅是愚蠢的运气。
这是一个专业的过程吗?
你可能会认为这个过程不实用或不专业。我认为,当你刚开始时,你需要不同于那些编程时间很长的人所需的工具。我可以坐下来想一个点子然后编码,但我已经从事专业编程的时间比你活了的时间还长。然而,在我的脑海中,这基本上是我遵循的过程。我只是在脑海中迅速地做这个过程,而你必须在外部练习直到内化。
当我卡住或者在学习一门新语言时,我会使用这个过程。如果我不懂一门语言但知道我想做什么,那么我通常可以写注释然后慢慢将其转换为代码,这也教会我那种语言。我和你之间唯一的区别是,由于多年的训练,我做得更快。
关于“X/Y”非问题
一些专业人士声称,这个过程会让学生患上一种奇怪的疾病,称为“X/Y 问题”。他们将 X/Y 问题描述为“有人想做 X,但只知道如何做 Y,所以他们请求帮助如何做 Y。” X/Y 问题的问题在于它批评了那些简单学习编程的人,并没有提出解决方案。对于“X/Y 问题的讨厌者”,解决方案似乎是“已经知道答案”,因为如果他们知道如何做 X,他们就不会去烦恼 Y。这种信念的虚伪之处在于所有讨厌这种问题的人都经历过这个阶段,提出过这些完全相同的“X/Y”问题。
另一个问题是,他们在责备你的糟糕文档。经典的例子来自 X/Y 问题的原始描述:
1 <n00b> How can I echo the last three characters in a filename?
2
3 <feline> If they're in a variable: echo ${foo: -3}
4 <feline> Why 3 characters? What do you REALLY want?
5 <feline> Do you want the extension?
6
7 <n00b> Yes.
8
9 <feline> Then ASK FOR WHAT YOU WANT!
10 <feline> There's no guarantee that every filename will
11 have a three-letter extension,
12 <feline> so blindly grabbing three characters does not
13 solve the problem.
14 <feline> echo ${foo##*.}
首先,这个feline
人实际上在一个专门回答问题的 IRC 频道里大声责骂某人提问。“要求你想要的东西!”第二个问题是,他们的解决方案是我——一个有几十年经验的 bash 和 Linux 专业人士——每次都要查找的东西。这是 bash 中最糟糕文档化、最不可用的功能之一。一个初学者如何能预先知道他们应该使用一些复杂的“dollar brace name pound pound asterisk dot brace”操作?如果在线有简单的文档解释如何做这个操作,这个人很可能不会提出这个问题。如果 bash 实际上有一个基本功能来执行这个每个人都需要的非常常见的操作,那将更好。
当涉及“X/Y 问题”时,这实际上只是一个借口,用来责骂初学者是初学者。每个声称讨厌这个问题的人要么根本不写代码,要么绝对在学习编程时确实做过这样的事情。这就是学习编程的方式。您遇到问题并通过学习如何实现解决方案来摸索解决方案。因此,如果遇到像<feline>
这样的人,只需忽略他们。他们只是借口找个人发火并感觉自己更优越。
此外,您会注意到在上一个对话中,没有一个人要求看代码。如果<n00b>
只是展示了他们的代码,那么<feline>
就可以推荐更好的方法来解决问题。问题解决了。我是说,假设<feline>
实际上能够编写代码,而不只是在 IRC 中等待着攻击毫无戒备的初学者提问。
if 语句规则
-
每个
if
语句必须有一个else
。 -
如果
else
部分永远不应该运行,因为这没有意义,那么你必须在else
中使用一个 die 函数,打印出错误消息并终止程序,就像我们在之前的练习中所做的那样。这将找到许多错误。 -
永远不要嵌套超过两层的
if
语句,并始终尝试将其保持一层。 -
将
if
语句视为段落,其中每个if-elif-else
组合就像一组句子。在其前后放置空行。 -
您的布尔测试应该简单。如果它们复杂,将它们的计算移到函数中的变量中,并为变量使用一个好的名称。
如果您遵循这些简单的规则,您将开始写出比大多数程序员更好的代码。回到上一个练习,看看我是否遵循了所有这些规则。如果没有,请纠正我的错误。
警告!
在现实生活中永远不要成为规则的奴隶。在训练过程中,您需要遵循这些规则以增强思维能力,但在现实生活中,有时这些规则只是愚蠢的。如果您认为某个规则很愚蠢,请尝试不使用它。
循环规则
-
仅在需要永久循环时才使用
while
循环,这意味着可能永远不会用到。这仅适用于 Python;其他语言不同。 -
对于所有其他类型的循环,请使用
for
循环,特别是在需要循环的事物数量是固定或有限的情况下。
调试提示
-
不要使用“调试器”。调试器就像对生病的人进行全身扫描一样。您不会得到任何具体有用的信息,而会发现许多无用且令人困惑的信息。
-
调试程序的最佳方法是使用
print
打印出程序中变量的值,以查看它们出错的位置。 -
确保程序的各个部分在编写时能够正常工作。不要在尝试运行之前编写大量的代码文件。少写一点,运行一点,修复一点。
作业
现在编写一个类似于我在上一个练习中创建的游戏。它可以是你想要的任何类型的游戏,但风格相同。花一周的时间让它尽可能有趣。在学习练习中,尽可能使用列表、函数和模块(还记得练习 13 中的那些吗?),并找到尽可能多的新的 Python 片段来使游戏运行。
在开始编码之前,你必须为你的游戏绘制一张地图。在编码之前,先在纸上创建玩家必须经过的房间、怪物和陷阱。
有了地图后,尝试着编写代码。如果在地图中发现问题,那就调整它,使代码与之匹配。
在软件开发中,最好的方法是像这样分成小块:
-
在一张纸上或一张索引卡上,写下你需要完成的任务列表,以完成软件开发。这就是你的待办事项清单。
-
从你的清单中选择最容易的任务。
-
在你的源文件中写下英文注释,作为你在代码中如何完成这个任务的指南。
-
在英文注释下面写一些代码。
-
快速运行你的脚本,看看代码是否有效。
-
保持在写一些代码、运行测试并修复直到它有效的循环中工作。
-
将这个任务从你的清单上划掉,然后选择下一个最容易的任务并重复。
这个过程将帮助你以一种系统和一致的方式来开发软件。在工作时,通过删除你实际不需要的任务并添加你需要的任务来更新你的清单。
练习 37:符号复习
现在是时候复习你所知道的符号和 Python 关键字,并尝试在接下来的几节课中学习更多。我已经列出了所有重要的 Python 符号和关键字。
在这节课中,首先尝试从记忆中写出每个关键字的作用。接下来,在网上搜索它们,看看它们真正的作用。这可能很困难,因为有些很难搜索,但无论如何都要尝试。
如果你从记忆中记错了其中一个,就制作一张正确定义的索引卡,尝试“纠正”你的记忆。
最后,在一个小的 Python 程序中使用这些中的每一个,或者尽可能多地完成。目标是找出符号的作用,确保你理解正确,如果不正确就纠正,然后使用它来牢记。
关键字
数据类型
对于数据类型,写出每种数据类型的组成部分。例如,对于字符串,写出如何创建一个字符串。对于数字,写出一些数字。
字符串转义序列
对于字符串转义序列,将它们用在字符串中,确保它们执行你认为的操作。
旧式字符串格式
对于字符串格式也是一样:在一些字符串中使用它们,以了解它们的作用。
旧版 Python 2 代码使用这些格式化字符来实现 f-strings 的功能。尝试它们作为替代方案。
运算符
其中一些可能对你来说很陌生,但无论如何都要查找它们。找出它们的作用,如果你仍然无法弄清楚,就留到以后再看。
大约花一周的时间,但如果你更快完成,那就太好了。重点是尝试覆盖所有这些符号,并确保它们牢记在你的脑海中。同样重要的是找出你不知道的东西,这样你就可以以后修复它。
阅读代码
现在找一些 Python 代码来阅读。你应该阅读任何你能找到的 Python 代码,并尝试窃取你发现的想法。你实际上应该有足够的知识来阅读,但也许不理解代码的作用。这节课教你如何应用你学到的东西来理解别人的代码。
首先,打印出你想要理解的代码。是的,打印出来,因为你的眼睛和大脑更习惯于阅读纸张而不是电脑屏幕。确保每次打印几页。
其次,浏览你的打印输出,并对以下内容做笔记:
-
函数及其作用。
-
每个变量首次被赋值的地方。
-
程序中不同部分中具有相同名称的任何变量。这些以后可能会有麻烦。
-
没有
else
子句的if
语句。它们正确吗? -
任何可能不会结束的
while
循环。 -
任何你因为任何原因无法理解的代码部分。
第三,一旦你标记了所有这些,尝试通过写注释来向自己解释。解释函数,它们如何被使用,涉及哪些变量以及你可以找出这段代码的任何内容。
最后,在所有困难的部分,逐行追踪每个变量的值,逐个函数地。实际上,再做一份打印输出,并在边缘写下你需要“追踪”的每个变量的值。
一旦你对代码的功能有了很好的理解,回到电脑上再次阅读它,看看是否能发现新的东西。继续找到更多的代码并这样做,直到你不再需要打印输出为止。
学习练习
-
找出“流程图”是什么,并画几个。
-
如果你在阅读代码时发现错误,请尝试修复它们,并将更改发送给作者。
-
当你不使用纸张时的另一种技巧是在代码中用
#
注释来记录你的笔记。有时,这些注释可能成为实际的注释,帮助下一个人。
常见学生问题
我该如何在网上搜索这些内容? 只需在你想要查找的任何内容前加上“python3”。例如,要查找yield
,搜索python3 yield
。