练习 19:函数和变量
现在你将把函数与你从之前练习中了解到的变量结合起来。如你所知,变量给数据片段一个名称,这样你就可以在程序中使用它。如果你有这段代码:
1 x = 10
然后你创建了一个名为x
的数据片段,它等于数字 10。
你还知道你可以像这样带参数调用函数:
1 def print_one(arg1): 2 print(f"arg1: {arg1}")
参数arg1
是类似于之前的x
的变量,只是当你像这样调用函数时为你创建:
1 print_one("First!")
在练习 18 中,你学习了当你调用函数时 Python 如何运行它们,但如果你这样做会发生什么:
1 y = "First!" 2 print_one(y)
不直接使用"First!"
调用print_one
,而是将"First!"
赋给y
,然后将y
传递给print_one
。这样会起作用吗?这里有一小段代码示例,你可以在 Jupyter 中测试一下:
1 def print_one(arg1): 2 print(f"arg1: {arg1}") 3 4 y = "First!" 5 print_one(y)
这展示了如何将变量的概念y = "First!"
与调用使用这些变量的函数相结合。在进行这个较长的练习之前,研究这个并尝试自己的变化,但首先给一点建议:
- 这个很长,如果你在 Jupyter 中觉得难以管理,那么尝试将其输入到一个
ex19.py
文件中在终端中运行。 - 通常情况下,你应该一次只输入几行代码,但如果你只输入函数的第一行,你会遇到问题。你可以使用
pass
关键字来解决这个问题,像这样:def some_func(some_arg): pass
。pass
关键字是用来创建一个空函数而不会引发错误的方法。 - 如果你想看到每个函数在做什么,你可以使用“调试打印”像这样:
print
(">>>> 我在这里", something)
。这将打印出一条消息,帮助你“跟踪”代码并查看每个函数中的something
是什么。
列表 19.1:ex19.py
1 def cheese_and_crackers(cheese_count, boxes_of_crackers): 2 print(f"You have {cheese_count} cheeses!") 3 print(f"You have {boxes_of_crackers} boxes of crackers!") 4 print("Man that's enough for a party!") 5 print("Get a blanket.\n") 6 7 8 print("We can just give the function numbers directly:") 9 cheese_and_crackers(20, 30) 10 11 12 print("OR, we can use variables from our script:") 13 amount_of_cheese = 10 14 amount_of_crackers = 50 15 16 cheese_and_crackers(amount_of_cheese, amount_of_crackers) 17 18 19 print("We can even do math inside too:") 20 cheese_and_crackers(10 + 20, 5 + 6) 21 22 23 print("And we can combine the two, variables and math:") 24 cheese_and_crackers(amount_of_cheese + 100, amount_of_crackers + 1000)
你应该看到的内容
1 We can just give the function numbers directly: 2 You have 20 cheeses! 3 You have 30 boxes of crackers! 4 Man that's enough for a party! 5 Get a blanket. 6 7 OR, we can use variables from our script: 8 You have 10 cheeses! 9 You have 50 boxes of crackers! 10 Man that's enough for a party! 11 Get a blanket. 12 13 We can even do math inside too: 14 You have 30 cheeses! 15 You have 11 boxes of crackers! 16 Man that's enough for a party! 17 Get a blanket. 18 19 And we can combine the two, variables and math: 20 You have 110 cheeses! 21 You have 1050 boxes of crackers! 22 Man that's enough for a party! 23 Get a blanket.
学习练习
- 你记得一次只输入几行代码吗?在填充之前使用
pass
创建一个空函数了吗?如果没有,删除你的代码然后重新做一遍。 - 将
cheese_and_crackers
的名称拼错,然后查看错误消息。现在修复它。 - 删除数学中的一个
+
符号,看看你会得到什么错误。 - 修改数学内容,然后尝试预测你将得到什么输出。
- 更改变量并尝试猜测这些更改后的输出。
常见学生问题
这个练习目前还没有问题,但你可以通过 help@learncodethehardway.org 向我提问以获取帮助。也许你的问题会出现在这里。
练习 20:函数和文件
记住函数的清单,然后在这个练习中要特别注意函数和文件如何一起工作以制作有用的东西。你还应该继续在运行代码之前只输入几行。如果发现自己输入了太多行,请删除它们然后重新输入。这样做可以使用python
来训练你对 Python 的理解。
这是这个练习的代码。再次强调,如果你觉得 Jupyter 难以使用,那么写一个ex20.py
文件并以这种方式运行它。
列表 20.1: ex20.py
1 from sys import argv 2 3 script, input_file = argv 4 5 def print_all(f): 6 print(f.read()) 7 8 def rewind(f): 9 f.seek(0) 10 11 def print_a_line(line_count, f): 12 print(line_count, f.readline()) 13 14 current_file = open(input_file) 15 16 print("First let's print the whole file:\n") 17 18 print_all(current_file) 19 20 print("Now let's rewind, kind of like a tape.") 21 22 rewind(current_file) 23 24 print("Let's print three lines:") 25 26 current_line = 1 27 print_a_line(current_line, current_file) 28 29 current_line = current_line + 1 30 print_a_line(current_line, current_file) 31 32 current_line = current_line + 1 33 print_a_line(current_line, current_file)
注意每次运行print_a_line
时我们如何传入当前行号。在这个练习中没有什么新的。它有函数,你知道那些。它有文件,你也知道那些。只要花点时间,你就能理解。
你应该看到的内容
1 First let's print the whole file: 2 3 This is line 1 4 This is line 2 5 This is line 3 6 7 Now let's rewind, kind of like a tape. 8 Let's print three lines: 9 1 This is line 1 10 11 2 This is line 2 12 13 3 This is line 3
学习扩展
- 为每一行写英文注释以理解该行的作用。
- 每次运行
print_a_line
时,你都会传入一个变量current_line
。写出每个函数调用中current_line
等于什么,并跟踪它如何变成print_a_line
中的line_count
。 - 找到每个函数被使用的地方,并检查其
def
以确保你给出了正确的参数。 - 在线研究
file
的seek
函数是做什么的。尝试pydoc file
,看看能否从中弄清楚。然后尝试pydoc file.seek
来查看seek
的作用。 - 研究
+=
的简写表示法,并重写脚本以使用+=
。
常见学生问题
print_all
和其他函数中的f
是什么? f
就像你在练习 18 中的其他函数中一样,只是这次它是一个文件。在 Python 中,文件有点像主机上的旧磁带驱动器或者可能是 DVD 播放器。它有一个“读头”,你可以“寻找”这个读头在文件中的位置,然后在那里进行操作。每次你执行f.seek(0)
时,你都在移动到文件的开头。每次你执行f.readline()
时,你都在从文件中读取一行,并将读头移动到结束该行的\n
之后。随着你的学习,这将会有更多解释。
为什么seek(0)
不会将current_line
设置为 0? 首先,seek()
函数处理的是字节,而不是行。代码seek(0)
将文件移动到文件中的第 0 字节(第一个字节)。其次,current_line
只是一个变量,与文件没有真正的连接。我们是手动递增它。
+=
是什么? 你知道在英语中我可以将“it is”重写为“it’s”吗?或者我可以将“you are”重写为“you’re”吗?在英语中,这被称为“缩略形式”,这有点像=
和+
两个操作的缩写。这意味着x = x + y
与x += y
是相同的。
readline()
如何知道每行在哪里?在 readline()
内部有代码扫描文件的每个字节,直到找到一个 \n
字符,然后停止读取文件并返回到目前为止找到的内容。文件 f
负责在每次 readline()
调用后维护文件中的当前位置,以便继续读取每一行。
文件中的行之间为什么有空行?readline()
函数返回文件中该行末尾的 \n
。在 print
函数调用的末尾添加 end = ""
可以避免在每行末尾添加双重 \n
。
练习 21:函数可以返回值
你一直在使用=
字符来命名变量并将它们设置为数字或字符串。现在我们将再次让你大开眼界,向你展示如何使用=
和一个新的 Python 词return
来将变量设置为来自函数的值。有一件事要特别注意,但首先输入这个:
代码清单 21.1: ex21.py
1 def add(a, b): 2 print(f"ADDING {a} + {b}") 3 return a + b 4 5 def subtract(a, b): 6 print(f"SUBTRACTING {a} - {b}") 7 return a - b 8 9 def multiply(a, b): 10 print(f"MULTIPLYING {a} * {b}") 11 return a * b 12 13 def divide(a, b): 14 print(f"DIVIDING {a} / {b}") 15 return a / b 16 17 18 print("Let's do some math with just functions!") 19 20 age = add(30, 5) 21 height = subtract(78, 4) 22 weight = multiply(90, 2) 23 iq = divide(100, 2) 24 25 print(f"Age: {age}, Height: {height}, Weight: {weight}, IQ: {iq}") 26 27 28 # A puzzle for the extra credit, type it in anyway. 29 print("Here is a puzzle.") 30 31 what = add(age, subtract(height, multiply(weight, divide(iq, 2)))) 32 33 print("That becomes: ", what, "Can you do it by hand?")
现在我们正在为add
、subtract
、multiply
和divide
做我们自己的数学函数。需要注意的重要一点是我们说的最后一行return a + b
(在add
中)。这样做的效果如下:
- 我们的函数被调用时带有两个参数:
a
和b
。 - 我们打印出我们的函数正在做的事情,在这种情况下是“ADDING”。
- 然后我们告诉 Python 做一些有点反向的事情:我们返回
a + b
的加法。你可以这样说,“我将a
和b
相加然后返回它们。” - Python 将这两个数字相加。然后当函数结束时,运行它的任何行都可以将
a + b
的结果赋给一个变量。
就像本书中的许多其他内容一样,你应该慢慢来,分解问题,并尝试追踪发生了什么。为了帮助,有额外的练习来解决一个谜题并学到一些有趣的东西。
你应该看到的结果
1 Let's do some math with just functions! 2 ADDING 30 + 5 3 SUBTRACTING 78 - 4 4 MULTIPLYING 90 * 2 5 DIVIDING 100 / 2 6 Age: 35, Height: 74, Weight: 180, IQ: 50.0 7 Here is a puzzle. 8 DIVIDING 50.0 / 2 9 MULTIPLYING 180 * 25.0 10 SUBTRACTING 74 - 4500.0 11 ADDING 35 + -4426.0 12 That becomes: -4391.0 Can you do it by hand?
学习扩展
- 如果你不确定
return
的作用,尝试编写一些自己的函数,并让它们返回一些值。你可以返回任何可以放在=
右侧的东西。 - 脚本的结尾是一个谜题。我正在将一个函数的返回值作为另一个函数的参数。我正在以链式方式执行这个操作,所以我有点像使用函数创建一个公式。看起来很奇怪,但如果你运行脚本,你会看到结果。你应该尝试找出能够重新创建相同操作集的正常公式。
- 一旦你为谜题找到了公式,就深入其中,看看当你修改函数的部分时会发生什么。试着故意改变它以生成另一个值。
- 做相反的操作。编写一个简单的公式,并以相同的方式使用函数来计算它。
这个练习可能会让你的大脑混乱,但慢慢来,把它当作一个小游戏。像这样解决谜题是编程变得有趣的地方,所以在我们继续进行时,我会给你更多类似的小问题。
常见学生问题
为什么 Python 打印公式或函数“反向”? 它实际上不是反向的,而是“里外相反”。当你开始将函数分解为单独的公式和函数时,你会看到它是如何工作的。试着理解我所说的“里外相反”而不是“反向”。
我如何使用 input()
输入自己的值? 还记得int(input())
吗?问题在于你无法输入浮点数,所以也尝试使用float(input())
。
“写出一个公式”是什么意思? 尝试以24 + 34 / 100 - 1023
为起点。将其转换为使用函数。现在想出你自己类似的数学方程,并使用变量使其更像一个公式。
笨办法学 Python3 第五版(预览)(二)(2)https://developer.aliyun.com/article/1483431