笨办法学 Python3 第五版(预览)(三)

原文: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语句是什么以及它的作用是什么。在继续下一个练习之前,尝试用自己的话回答这些问题:

  1. 你认为if对下面的代码有什么影响?

  2. 为什么if下面的代码需要缩进四个空格?

  3. 如果没有缩进会发生什么?

  4. 你能否在if语句中放入来自练习 28 的其他布尔表达式?试一试。

  5. 如果你改变peoplecatsdogs的初始值会发生什么?

常见学生问题

+=是什么意思? 代码x += 1x = x + 1相同,但输入更少。你可以称之为“递增运算符”。对于-=和许多其他表达式,你以后会学到的也是一样。

练习 31:否则和如果

在上一个练习中,你解决了一些if 语句,然后试图猜测它们是什么以及它们如何工作。在学习更多之前,我将通过回答你在学习练习中提出的问题来解释一切。你做了学习练习,对吧?

  1. 你认为if对其下面的代码有什么影响?if 语句在代码中创建了所谓的“分支”。这有点像那些选择你自己冒险的书,如果你做出一个选择,就会被要求翻到一页,如果你选择另一条路,就会翻到另一页。if 语句告诉你的脚本,“如果这个布尔表达式为真,则运行其下的代码;否则跳过它。”

  2. 为什么if下面的代码需要缩进四个空格?在一行的末尾加上冒号是告诉 Python 你将创建一个新的代码“块”,然后缩进四个空格告诉 Python 哪些代码行在该块中。这与你在本书的前半部分创建函数时所做的事情完全相同。

  3. 如果没有缩进会发生什么?如果没有缩进,你很可能会产生 Python 错误。Python 希望你在以:(冒号)结尾的行之后缩进一些东西。

  4. 你能把练习 28 中的其他布尔表达式放在if 语句中吗?试试看。是的,你可以,而且它们可以尽可能复杂,尽管非常复杂的东西通常是不好的风格。

  5. 如果更改peoplecatsdogs的初始值会发生什么?因为你正在比较数字,如果更改数字,不同的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 的新方法。

学习练习

  1. 试着猜猜elifelse在做什么。

  2. 更改carspeopletrucks的数字,然后跟踪每个if 语句,看看将打印出什么。

  3. 尝试一些更复杂的布尔表达式,比如cars > people or trucks < cars

  4. 在每行上方写出该行的英文描述。

常见学生问题

如果多个 elif 块都为 True会发生什么? Python 从顶部开始运行第一个为True的块,因此只会运行第一个。

练习 32:做决策

在这本书的前半部分,你主要只是打印出一些称为“函数”的东西,但一切基本上都是直线的。你的脚本从顶部开始运行,一直到底部结束。如果你创建了一个函数,你可以稍后运行该函数,但它仍然没有你真正需要做出决策的分支。现在你有了 ifelseelif,你可以开始编写决策性的脚本了。

在上一个脚本中,你列出了一组简单的测试,询问一些问题。在这个脚本中,你将询问用户问题,并根据他们的答案做出决定。编写这个脚本,然后多玩一下,弄清楚它的运行方式。

代码清单 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 的工作原理。再次强调,如果这让你困惑,可以先跳过,以后再尝试。

练习

  1. 制作游戏的新部分,并改变人们可以做出的决定。在游戏变得荒谬之前尽可能扩展游戏。

  2. 编写一个全新的游戏。也许你不喜欢这个,那就自己创造一个。这是你的电脑;做你想做的事情。

常见学生问题

你能用一系列 if-else 组合替换 elif 吗? 在某些情况下可以,但这取决于每个 if/else 的编写方式。这也意味着 Python 将检查每个 if-else 组合,而不像 if-elif-else 那样只检查第一个为假的条件。尝试创建一些来了解差异。

如何判断一个数字是否在一系列数字范围内? 你有两个选择:使用 0 < x < 101 <= x < 10—这是经典的表示法—或使用 x in range(1, 10)

如果我想在 if-elif-else 块中增加更多选项怎么办? 为每个可能的选择添加更多 elif 块。

练习 33:循环和列表

现在你应该能够编写一些更有趣的程序了。如果你一直在跟进,你应该意识到现在你可以将所有其他学到的东西与if-statements和布尔表达式结合起来,使你的程序做一些聪明的事情。

然而,程序也需要快速地执行重复的事情。在这个练习中,我们将使用for-loop来构建和打印各种列表。当你做这个练习时,你会开始明白它们是什么。我现在不会告诉你。你必须自己弄清楚。

在使用for-loop之前,你需要一种方法来存储循环的结果。最好的方法是使用listsLists正是它们的名字所说的:一个按照从头到尾顺序组织的东西的容器。这并不复杂;你只需要学习一种新的语法。首先,这是如何创建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工作:

  1. 调用the_count.__next__()

  2. 如果the_count中没有更多元素,则跳转到 18

  3. 如果仍然有元素,则继续执行

  4. STORE_NAME然后将the_count.__next__()的结果赋给名为number的变量

这就是for-loop实际上所做的一切。它主要是一个单字节代码FOR_ITER,结合其他几个来遍历列表。

学习练习

  1. 看看你如何使用了range。查阅range函数以了解它。

  2. 在第 22 行完全避免了那个for-loop,直接将range(0,6)赋给elements,你能做到吗?

  3. 查找关于列表的 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-loopwhile-loop会持续执行其下的代码块,只要布尔表达式为True

等等,你一直跟上术语了吗?如果我们写一行并以:(冒号)结尾,那告诉 Python 开始一个新的代码块?然后我们缩进,这就是新代码。这一切都是关于构建你的程序,让 Python 知道你的意图。如果你没有理解这个概念,那就回去多做一些关于if语句、函数和for循环的工作,直到你理解为止。

后面我们会有一些练习,训练你的大脑阅读这些结构,类似于我们如何将布尔表达式烙印在你的大脑中。

回到while-loop。它们的作用就像一个if语句的测试,但不同于只运行代码块一次,它们会跳回到while所在的“顶部”,并重复。while循环会一直运行,直到表达式为False

while循环的问题在于:有时它们不会停止。如果你的意图只是一直循环直到宇宙的尽头,那么这很好。否则,你几乎总是希望你的循环最终会结束。

为了避免这些问题,有一些规则需要遵循:

  1. 确保你谨慎使用while循环。通常for循环更好。

  2. 检查你的while语句,并确保布尔测试最终会变为False

  3. 如果有疑问,在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)文档中查找所有的字节码。祝你好运!

学习练习

  1. 将这个while-loop转换为一个可以调用的函数,并用一个变量替换测试中的6i < 6)。

  2. 使用这个函数来重写脚本以尝试不同的数字。

  3. 在函数参数中添加另一个变量,你可以传入它,以便你可以更改第 8 行的+ 1,这样你就可以改变增量是多少。

  4. 再次重写脚本以使用这个函数,看看会有什么影响。

  5. 重写它以使用for-loopsrange。你还需要在中间保留增量器吗?如果不去掉它会发生什么?

如果在任何时候你这样做时出现问题(很可能会),只需按住CTRL并按下cCTRL-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!

学习练习

  1. 绘制游戏地图以及你如何在其中流动。

  2. 修复所有错误,包括拼写错误。

  3. 为你不理解的函数写注释。

  4. 添加更多内容到游戏中。你能做些什么来简化和扩展它?

  5. 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循环的规则,这将帮助你避免麻烦。我还会给你一些关于调试的提示,这样你就可以找出程序中的问题。最后,你将设计一个类似于上一个练习但有些不同的小游戏。

从想法到可运行的代码

有一个简单的过程任何人都可以遵循,将你的想法转化为代码。这不是唯一的过程,但对许多人来说效果很好。在你开发自己的个人过程之前,使用这个过程。

  1. 以你理解的任何形式将你的想法表达出来。你是作家吗?那就写一篇关于你的想法的文章。你是艺术家或设计师吗?那就画出用户界面。你喜欢图表吗?看看序列图,这是编程中最有用的图之一。

  2. 为你的代码创建一个文件。是的,信不信由你,这是一个重要的步骤,大多数人都会遇到困难。如果你想不出一个名字,就随便挑一个吧。

  3. 用简单的英语(或者你最容易理解的语言)写下你的想法的描述作为注释。

  4. 从顶部开始,将第一个注释转换为“伪代码”,这有点像 Python,但你不用在意语法。

  5. 将那个“伪代码”转换为真正的 Python 代码,并不断运行你的文件,直到这段代码实现了你的注释所说的。

  6. 重复这个过程,直到你将所有的注释转换为 Python 代码。

  7. 退一步,审查你的代码,然后删除它。你不必一直这样做,但如果你养成丢弃第一个版本的习惯,你将获得两个好处:

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 语句规则

  1. 每个if语句必须有一个else

  2. 如果else部分永远不应该运行,因为这没有意义,那么你必须在else中使用一个 die 函数,打印出错误消息并终止程序,就像我们在之前的练习中所做的那样。这将找到许多错误。

  3. 永远不要嵌套超过两层的if语句,并始终尝试将其保持一层。

  4. if语句视为段落,其中每个if-elif-else组合就像一组句子。在其前后放置空行。

  5. 您的布尔测试应该简单。如果它们复杂,将它们的计算移到函数中的变量中,并为变量使用一个好的名称。

如果您遵循这些简单的规则,您将开始写出比大多数程序员更好的代码。回到上一个练习,看看我是否遵循了所有这些规则。如果没有,请纠正我的错误。

警告!

在现实生活中永远不要成为规则的奴隶。在训练过程中,您需要遵循这些规则以增强思维能力,但在现实生活中,有时这些规则只是愚蠢的。如果您认为某个规则很愚蠢,请尝试不使用它。

循环规则

  1. 仅在需要永久循环时才使用while循环,这意味着可能永远不会用到。这仅适用于 Python;其他语言不同。

  2. 对于所有其他类型的循环,请使用for循环,特别是在需要循环的事物数量是固定或有限的情况下。

调试提示

  1. 不要使用“调试器”。调试器就像对生病的人进行全身扫描一样。您不会得到任何具体有用的信息,而会发现许多无用且令人困惑的信息。

  2. 调试程序的最佳方法是使用print打印出程序中变量的值,以查看它们出错的位置。

  3. 确保程序的各个部分在编写时能够正常工作。不要在尝试运行之前编写大量的代码文件。少写一点,运行一点,修复一点。

作业

现在编写一个类似于我在上一个练习中创建的游戏。它可以是你想要的任何类型的游戏,但风格相同。花一周的时间让它尽可能有趣。在学习练习中,尽可能使用列表、函数和模块(还记得练习 13 中的那些吗?),并找到尽可能多的新的 Python 片段来使游戏运行。

在开始编码之前,你必须为你的游戏绘制一张地图。在编码之前,先在纸上创建玩家必须经过的房间、怪物和陷阱。

有了地图后,尝试着编写代码。如果在地图中发现问题,那就调整它,使代码与之匹配。

在软件开发中,最好的方法是像这样分成小块:

  1. 在一张纸上或一张索引卡上,写下你需要完成的任务列表,以完成软件开发。这就是你的待办事项清单。

  2. 从你的清单中选择最容易的任务。

  3. 在你的源文件中写下英文注释,作为你在代码中如何完成这个任务的指南。

  4. 在英文注释下面写一些代码。

  5. 快速运行你的脚本,看看代码是否有效。

  6. 保持在写一些代码、运行测试并修复直到它有效的循环中工作。

  7. 将这个任务从你的清单上划掉,然后选择下一个最容易的任务并重复。

这个过程将帮助你以一种系统和一致的方式来开发软件。在工作时,通过删除你实际不需要的任务并添加你需要的任务来更新你的清单。

练习 37:符号复习

现在是时候复习你所知道的符号和 Python 关键字,并尝试在接下来的几节课中学习更多。我已经列出了所有重要的 Python 符号和关键字。

在这节课中,首先尝试从记忆中写出每个关键字的作用。接下来,在网上搜索它们,看看它们真正的作用。这可能很困难,因为有些很难搜索,但无论如何都要尝试。

如果你从记忆中记错了其中一个,就制作一张正确定义的索引卡,尝试“纠正”你的记忆。

最后,在一个小的 Python 程序中使用这些中的每一个,或者尽可能多地完成。目标是找出符号的作用,确保你理解正确,如果不正确就纠正,然后使用它来牢记。

关键字

图片图片

数据类型

对于数据类型,写出每种数据类型的组成部分。例如,对于字符串,写出如何创建一个字符串。对于数字,写出一些数字。

图片

字符串转义序列

对于字符串转义序列,将它们用在字符串中,确保它们执行你认为的操作。

图片

旧式字符串格式

对于字符串格式也是一样:在一些字符串中使用它们,以了解它们的作用。

图片

旧版 Python 2 代码使用这些格式化字符来实现 f-strings 的功能。尝试它们作为替代方案。

运算符

其中一些可能对你来说很陌生,但无论如何都要查找它们。找出它们的作用,如果你仍然无法弄清楚,就留到以后再看。

图片

大约花一周的时间,但如果你更快完成,那就太好了。重点是尝试覆盖所有这些符号,并确保它们牢记在你的脑海中。同样重要的是找出你知道的东西,这样你就可以以后修复它。

阅读代码

现在找一些 Python 代码来阅读。你应该阅读任何你能找到的 Python 代码,并尝试窃取你发现的想法。你实际上应该有足够的知识来阅读,但也许不理解代码的作用。这节课教你如何应用你学到的东西来理解别人的代码。

首先,打印出你想要理解的代码。是的,打印出来,因为你的眼睛和大脑更习惯于阅读纸张而不是电脑屏幕。确保每次打印几页。

其次,浏览你的打印输出,并对以下内容做笔记:

  1. 函数及其作用。

  2. 每个变量首次被赋值的地方。

  3. 程序中不同部分中具有相同名称的任何变量。这些以后可能会有麻烦。

  4. 没有else子句的if语句。它们正确吗?

  5. 任何可能不会结束的while循环。

  6. 任何你因为任何原因无法理解的代码部分。

第三,一旦你标记了所有这些,尝试通过写注释来向自己解释。解释函数,它们如何被使用,涉及哪些变量以及你可以找出这段代码的任何内容。

最后,在所有困难的部分,逐行追踪每个变量的值,逐个函数地。实际上,再做一份打印输出,并在边缘写下你需要“追踪”的每个变量的值。

一旦你对代码的功能有了很好的理解,回到电脑上再次阅读它,看看是否能发现新的东西。继续找到更多的代码并这样做,直到你不再需要打印输出为止。

学习练习

  1. 找出“流程图”是什么,并画几个。

  2. 如果你在阅读代码时发现错误,请尝试修复它们,并将更改发送给作者。

  3. 当你不使用纸张时的另一种技巧是在代码中用#注释来记录你的笔记。有时,这些注释可能成为实际的注释,帮助下一个人。

常见学生问题

我该如何在网上搜索这些内容? 只需在你想要查找的任何内容前加上“python3”。例如,要查找yield,搜索python3 yield

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/431911.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

gin gorm学习笔记

代码仓库 https://gitee.com/zhupeng911/go-advanced.git https://gitee.com/zhupeng911/go-project.git 1. gin介绍 Gin 是使用纯 Golang 语言实现的 HTTP Web框架&#xff0c;Gin接口设计简洁&#xff0c;提供类似Martini的API&#xff0c;性能极高&#xff0c;现在被广泛使用…

基于ZYNQ PS-SPI的Flash驱动开发

本文使用PS-SPI实现Flash读写&#xff0c;PS-SPI的基础资料参考Xilinx UG1085的文档说明&#xff0c;其基础使用方法是&#xff0c;配置SPI模式&#xff0c;控制TXFIFO/RXFIFO&#xff0c;ZYNQ的IP自动完成发送TXFIFO数据&#xff0c;接收数据到RXFIFO&#xff0c;FIFO深度为12…

Vmware Workstation 不可恢复错误:0xc0000005 has occured

上周打开虚拟机的时候报错&#xff1a;Vmware Workstation 不可恢复错误&#xff1a;0xc0000005 has occured&#xff0c;查看网上资料说是vmware版本太低&#xff0c;需要手动更新本地版本。 由于本地网络不是很好&#xff0c;没能正常更新&#xff0c;无意中出现问题前更改了…

概率基础——极大似然估计

概率基础——极大似然估计 引言 极大似然估计&#xff08;Maximum Likelihood Estimation&#xff0c;简称MLE&#xff09;是统计学中最常用的参数估计方法之一&#xff0c;它通过最大化样本的似然函数来估计参数值&#xff0c;以使得样本出现的概率最大化。极大似然估计在各…

docker单节点搭建在线商城

本文档使用到的软件包以上传到资源中 目录 1. 创建容器并配置基础内容 1.1 将gpmall-repo上传到容器中 1.2 添加yum源 2. 安装基础服务 2.1 安装JAVA环境 2.2 安装Redis缓存服务 2.3 安装Elasticsearch服务 2.4 安装Nginx服务 2.5 安装MariaDB数据库 2.6 安…

数据库分库分表中间件选择

目前分库分表的中间件有三种设计思路&#xff0c;分别是&#xff1a; 采用分散式架构&#xff0c;适用于用Java开发的高性能轻量级OLTP应用程序&#xff0c;以Sharding-JDBC为代表。采用中间层Proxy架构&#xff0c;提供了静态输入和所有语言支持&#xff0c;适用于OLAP应用程…

验证Tomcat进程是否启动成功 ps -ef | grep tomcat

验证Tomcat启动是否成功&#xff0c;有多种方式&#xff1a; 查看启动日志 more /usr/local/apache-tomcat-9.0.86/logs/catalina.out tail -50 /usr/local/apache-tomcat-9.0.86/logs/catalina.out 查看进程 ps -ef | grep tomcat 注意&#xff1a; ps命令是linux下非常强…

《剑指offer》14--剪绳子(整数拆分)[C++]

目录 题目描述 贪心算法 输出结果 题目描述 把一根绳子剪成多段&#xff0c;并且使得每段的长度乘积最大。 给定一个正整数 n&#xff0c;将其拆分为至少两个正整数的和&#xff0c;并使这些整数的乘积最大化。 返回你可以获得的最大乘积。 示例 1: 输入: 2 输出: 1 解释:…

ZYNQ--PS_PL交互(AXI_HP)

AXI_HP接口 通过AXI_HP接口&#xff0c;可直接通过AXI_FULL协议向DDR中通过DMA传输数据。 BD设计 AXI_HP接口设置 AXI_Master代码 module axi_full_master #(parameter C_M_TARGET_SLAVE_BASE_ADDR 32h40000000,parameter integer C_M_AXI_BURST_LEN 16,parameter …

代码随想录算法训练营第22天|235.二叉搜索树的最近公共祖先、701.二叉搜索树中的插入操作、450.删除二叉搜索树中的节点

目录 一、力扣235.二叉搜索树的最近公共祖先1.1 题目1.2 思路1.3 代码 二、力扣701.二叉搜索树中的插入操作2.1 题目2.2 思路2.3 代码 三、力扣450.删除二叉搜索树中的节点3.1 题目3.2 思路3.3 代码3.4 总结 一、力扣235.二叉搜索树的最近公共祖先 1.1 题目 1.2 思路 利用二叉…

09-Linux部署Redis

Linux部署Redis 简介 Redis&#xff0c;全称为Remote Dictionary Server&#xff08;远程字典服务&#xff09;&#xff0c;是一个开源的、使用ANSI C语言编写的、支持网络连接的、基于内存的、同时支持持久化的日志型Key-Value数据库&#xff0c;并提供多种语言的API。 Red…

七、西瓜书——降维与度量学习

1.K近邻 k 近邻(k-Nearest Neighbor,简称 kNN)学习是一种常用的监督学习方法&#xff0c;其工作机制非常简单: 给定测试样本,基于某种距离度量找出训练集中与其最靠近的k个训练样本,然后基于这k个“邻居”的信息来进行预测&#xff0c;通常,在分类任务中可使用“投票法”&#…

$nextTick底层原理(详细) - vue篇

公众号&#xff1a;需要以下pdf&#xff0c;关注下方 2023已经过完了&#xff0c;让我们来把今年的面试题统计号&#xff0c;来备战明年的金三银四&#xff01;所以&#xff0c;不管你是社招还是校招&#xff0c;下面这份前端面试工程师高频面试题&#xff0c;请收好。 前言 n…

CUDA学习笔记04:向量之和

参考资料 CUDA编程模型系列二(向量操作)_哔哩哔哩_bilibili &#xff08;非常好的学习资料&#xff01;&#xff09; vs2019 随意新建一个空项目&#xff0c;按照之前的环境配置配好项目依赖&#xff1a; CUDA学习笔记02&#xff1a;测试程序hello world-CSDN博客 代码结构…

jitpack上传aar异常: ERROR: No build artifacts found

问题 如图所示&#xff0c;提示 ERROR: No build artifacts found 解决 无法找到artifacts的情况下&#xff0c;我们就需要手动添加artifacts 。 //maven-publish 插件的配置 // publishing 用于定义项目的发布相关配置 publishing {// 配置maven 仓库repositories { Repo…

5201B数据网络测试仪

|5201B数据网络测试仪| | 产品综述 | 电科思仪5201B便携式数据网络测试仪&#xff0c;集成高性能IP基础测试硬件平台&#xff0c;提供L2-L3流量测试及协议仿真解决方案&#xff0c;支持以太网报文线速生成与分析、统计、报文捕获&#xff0c;以及路由、接入、组播、数据中心等协…

item_fee-获得淘宝商品快递费用 API调用说明获取测试key

item_fee-获得淘宝商品快递费用 .通过传入商品id、区域id&#xff0c;来获取该商品的快递费用。 公共参数 点此获取API请求地址 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&a…

Linux系统的服务/进程

系统守护进程&#xff08;服务&#xff09; •服务就是运行在网络服务器上监听用户请求的进程 •服务是通过端口号来区分的 常见的服务及其对应的端口 1.ftp&#xff1a;21 FTP指的是文件传输协议&#xff0c;它是用于在计算机网络上进行文件传输的标准网络协议。通过FTP&am…

数字化转型导师坚鹏:成为数字化转型顾问 引领数字化美好未来

成为数字化转型顾问 引领数字化美好未来 ——数字化人才与企业的共赢之路 数字经济新时代&#xff0c;中国企业向数字化转型要效益&#xff1b; 转型顾问创未来&#xff0c;职场精英借数字化转型成良师。 我们中国政府特别重视数字经济发展及数字化人才培养。早在2020年8月2…

通过XML调用CAPL脚本进行测试(新手向)

目录 0 引言 1 XML简介 2 通过XML调用CAPL脚本 0 引言 纪念一下今天这个特殊日子&#xff0c;四年出现一次的29号。 在CANoe中做自动化测试常用的编程方法有CAPL和XML两种&#xff0c;二者各有各的特色&#xff0c;对于CAPL来说新手肯定是更熟悉一些&#xff0c;因为说到在C…