笨办法学 Python3 第五版(预览)(一)(3)https://developer.aliyun.com/article/1483425
我创建了一个名为ex15_sample.txt
的文件并运行了我的脚本。
1 Here's your file ex15_sample.txt: 2 This is stuff I typed into a file. 3 It is really cool stuff. 4 Lots and lots of fun to have in here. 5 6 7 Type the filename again: 8 > ex15_sample.txt 9 This is stuff I typed into a file. 10 It is really cool stuff. 11 Lots and lots of fun to have in here.
学习练习
这是一个很大的飞跃,所以在继续之前,请确保你尽力完成这些学习练习。
- 在每一行上面,用英语写出那行代码的作用。
- 如果你不确定,向别人寻求帮助或在网上搜索。很多时候搜索“python3 THING”会找到关于 Python 中那个 THING 做什么的答案。尝试搜索“python3 open”。
- 我在这里使用了“命令”这个词,但命令也被称为“函数”和“方法”。你将在本书后面学习有关函数和方法的知识。
- 删除使用
input
并再次运行脚本的 10-15 行。 - 只使用
input
,尝试以这种方式运行脚本。为什么有一种获取文件名的方式比另一种更好? - 启动
python3
以启动python3
shell,并从提示符中像在这个程序中一样使用open
。注意你如何可以在python3
中打开文件并运行read
? - 让你的脚本也在
txt
和txt_again
变量上调用close()
。在完成文件操作后关闭文件是很重要的。
常见学生问题
txt = open(filename)
是否返回文件的内容? 不,它不会。它实际上创建了一个叫做“文件对象”的东西。你可以把文件想象成 50 年代大型计算机上看到的旧磁带驱动器,甚至像今天的 DVD 播放器。你可以在其中移动,然后“读取”它们,但 DVD 播放器不是 DVD,就像文件对象不是文件内容一样。
我无法像你在第 7 个学习任务中说的那样在终端/PowerShell 中输入代码。首先,从命令行中只需输入python3
并按 Enter。现在你在python3
中,就像我们之前做过几次一样。然后你可以输入代码,Python 会逐段运行它。尝试一下。要退出,请输入quit()
并按 Enter。
为什么我们打开文件两次时没有错误? Python 不会限制你多次打开文件,有时这是必要的。
from sys import argv
是什么意思? 现在只需理解sys
是一个包,这个短语只是说从那个包中获取argv
功能。你以后会学到更多。
我将文件名放在 script, ex15_sample.txt = argv
中,但它不起作用。不,这不是你应该做的。确保代码与我的完全一样,然后像我一样从命令行运行它。你不需要放文件名进去;让 Python 放进去。
练习 16:读写文件
如果你做了上一个练习的学习练习,你应该看到各种可以给文件的命令(方法/函数)。这是我希望你记住的命令列表:
close
– 关闭文件。就像文本编辑器或文字处理器中的文件->保存..
一样。read
– 读取文件的内容。你可以将结果赋值给一个变量。readline
– 读取文本文件的一行。truncate
– 清空文件。如果你关心文件的话要小心。write('stuff')
– 将“stuff”写入文件。seek(0)
– 将读/写位置移动到文件的开头。
记住每个命令的一种方法是想象一下黑胶唱片、磁带、VHS 磁带、DVD 或 CD 播放器。在计算机的早期,数据存储在这些媒体上,所以许多文件操作仍然类似于一个线性存储系统。磁带和 DVD 驱动器需要“寻找”到一个特定的位置,然后你可以在那个位置读取或写入。今天我们有操作系统和存储介质模糊了随机存取内存和磁盘驱动器之间的界限,但我们仍然使用一个必须移动的线性磁带的读/写头的旧概念。
目前,这些是你需要知道的重要命令。其中一些需要参数,但我们并不真的关心。你只需要记住write
需要一个你想写入文件的字符串参数。
让我们利用其中的一些内容制作一个简单的文本编辑器:
列表 16.1: ex16.py
1 filename = "test.txt" 2 3 print(f"We're going to erase {filename}.") 4 print("If you don't want that, hit CTRL-C *(*^C*)*.") 5 print("If you do want that, hit RETURN.") 6 7 input("?") 8 9 print("Opening the file...") 10 target = open(filename, 'w') 11 12 print("Truncating the file. Goodbye!") 13 target.truncate() 14 15 print("Now I'm going to ask you for three lines.") 16 17 line1 = input("line 1: ") 18 line2 = input("line 2: ") 19 line3 = input("line 3: ") 20 21 print("I'm going to write these to the file.") 22 23 target.write(line1) 24 target.write("\n") 25 target.write(line2) 26 target.write("\n") 27 target.write(line3) 28 target.write("\n") 29 30 print("And finally, we close it.") 31 target.close()
那是一个很大的文件,可能是你输入的最大的文件。所以慢慢来,检查一下,经常运行,慢慢来。一个技巧是一次运行一部分。先运行 1-2 行,然后再运行两行,再运行几行,直到全部完成并运行。
你应该看到的内容
实际上你会看到两件事。首先是你新脚本的输出:
1 We're going to erase test.txt. 2 If you don't want that, hit CTRL-C (^C). 3 If you do want that, hit RETURN. 4 ? 5 Opening the file... 6 Truncating the file. Goodbye! 7 Now I'm going to ask you for three lines. 8 line 1: Mary had a little lamb 9 line 2: Its fleece was white as snow 10 line 3: It was also tasty 11 I'm going to write these to the file. 12 And finally, we close it.
现在,打开你创建的文件(在我的案例中是test.txt
),使用 Jupyter 的左侧面板查看一下。整洁,对吧?
学习练习
- 如果你不理解这个,回过头来,使用注释技巧来弄清楚。在每一行上加一个简单的英文注释将帮助你理解,或者至少让你知道你需要更多地研究。
- 编写一个类似于上一个练习 14 的
.py
脚本,使用read
(练习 15)和argv
(练习 13)来读取你刚刚创建的文件。确保你在终端/PowerShell 中运行而不是在 Jupyter 中。 - 这个文件中有太多的重复。使用字符串、格式和转义来用一个
target.write()
命令打印出line1
、line2
和line3
,而不是六个命令。 - 找出为什么我们需要将
'w'
作为open
的额外参数传递。提示:open
试图通过让你明确表示你要写入一个文件来保持安全。 - 如果以**'w’模式打开文件,那么你真的需要target.truncate()吗?阅读 Python 的open**函数的文档,看看这是否属实。
常见学生问题
在使用 ‘w’ 参数时, truncate()
是必需的吗? 参见学习任务 5。
‘w’ 代表什么? 实际上,它只是一个包含文件模式字符的字符串。如果使用**‘w’,那么你是在说“以‘写’模式打开此文件”,这就是’w’** 字符的原因。还有**'r’代表“读取”,‘a’**代表追加,以及这些的修饰符。
我可以使用哪些文件模式的修饰符? 现在最重要的是要知道**+修饰符,这样你就可以使用’w+','r+‘和’a+’**。这将以读取和写入模式打开文件,并根据使用的字符,以不同的方式定位文件。
只需执行 open(filename) 就会以 ‘r’ (读取)模式打开文件吗? 是的,这是**open()**函数的默认设置。
练习 17:更多文件
现在让我们做更多关于文件的事情。我们将编写一个 Python 脚本将一个文件复制到另一个文件。这将非常简短,但会给你关于文件的其他操作想法。
列表 17.1:ex17.py
1 from sys import argv 2 from os.path import exists 3 4 from_file = "test.txt" 5 to_file = "new_test.txt" 6 7 print(f"Copying from {from_file} to {to_file}") 8 9 # we could do these two on one line, how? 10 in_file = open(from_file) 11 indata = in_file.read() 12 13 print(f"The input file is {len*(*indata*)*} bytes long") 14 15 print(f"Does the output file exist? {exists*(*to_file*)*}") 16 print("Ready, hit RETURN to continue, CTRL-C to abort.") 17 input() 18 19 out_file = open(to_file, 'w') 20 out_file.write(indata) 21 22 print("Alright, all done.") 23 24 out_file.close() 25 in_file.close()
你应该立即注意到我们import
了另一个方便的命令叫做exists
。如果文件存在,它会返回True
,基于它的名称作为字符串参数。如果不存在,则返回False
。我们将在本书的后半部分使用这个函数来做很多事情,但现在你应该看看如何导入它。
使用import
是一种获取其他更好(通常是)程序员编写的大量免费代码的方法,这样你就不必自己编写它。
你应该看到的内容
就像你的其他脚本一样,用两个参数运行这个脚本:要复制的文件和要复制到的文件。我将使用一个名为test.txt
的简单测试文件:
1 Copying from test.txt to new_test.txt 2 The input file is 70 bytes long 3 Does the output file exist? True 4 Ready, hit RETURN to continue, CTRL-C to abort. 5 6 Alright, all done.
它应该适用于任何文件。尝试更多文件并看看会发生什么。只是要小心不要破坏重要文件。
警告!
你看到我用echo
制作文件和cat
显示文件的技巧了吗?你可以在附录 A“命令行速成课程”中学习如何做到这一点。
学习练习
- 这个脚本真的很烦人。在复制之前没有必要询问你,而且在屏幕上打印出太多内容。尝试通过删除功能使脚本更加友好易用。
- 看看你能把脚本做得多短。我可以让这一行很长。
- 注意在“你应该看到的内容”末尾我使用了一个叫做
cat
的东西?这是一个“连接”文件的旧命令,但主要是一个将文件打印到屏幕的简单方法。输入man cat
来了解它。 - 找出为什么你需要在代码中写
out_file.close()
。 - 去了解一下 Python 的
import
语句,并开始使用python3
尝试一下。尝试导入一些东西,看看你是否能做对。如果做不对也没关系。 - 尝试将这段代码转换为一个
ex17.py
脚本,你可以再次从终端/PowerShell 运行。如果你已经厌倦了 Jupyter 的文本编辑器,那么可以查看*First Steps*部分提到的编辑器。
常见学生问题
为什么 'w'
要加引号? 那是一个字符串。你已经使用字符串一段时间了。确保你知道什么是字符串。
不可能让这一行变成一行! 那个;取决于;你;如何;定义;一行;代码。
感觉这个练习很难是正常的吗? 是的,这是完全正常的。直到 Exercise 36,甚至可能在你完成这本书并用 Python 做一些东西之后,编程可能才会“点亮”。每个人都不同,所以继续前进,继续复习你遇到困难的练习,直到理解为止。要有耐心。
len()
函数是做什么的? 它获取你传递给它的字符串的长度,然后将其作为一个数字返回。试着玩一下。
当我试图缩短这个脚本时,在结尾关闭文件时出现错误。你可能做了类似这样的事情,indata = open(from_file).read()
,这意味着当你到达脚本结尾时就不需要再执行in_file.close()
。一旦那一行运行,Python 应该已经关闭了它。
我收到一个 Syntax:EOL while scanning string literal
错误。你忘记用引号正确结束一个字符串。再去看看那一行。
模块 2:编程基础
练习 18:名称、变量、代码、函数
大标题,对吧?我将要向你介绍函数!Dum dum dah!每个程序员都会滔滔不绝地谈论函数以及它们如何工作和做什么,但我会给你一个你现在可以使用的最简单的解释。
函数有三个作用:
- 它们命名代码片段的方式就像变量命名字符串和数字一样
- 它们接受参数的方式就像 Python 脚本在练习 13 中接受
argv
- 使用 1 和 2,它们让你制作自己的“迷你脚本”或“微型命令”
你可以像这样在 Python 中使用def
来创建一个空函数:
列表 18.1: ex18_demo.py
1 def do_nothing(): 2 pass
这创建了函数,但pass
关键字告诉 Python 这个函数是空的。要使函数执行某些操作,你需要在def
行下添加函数的代码,但要缩进四个空格:
列表 18.2: ex18_demo.py
1 def do_something(): 2 print("I did something!")
这实际上将代码print("I did something!")
分配给名称do_something
,这样你以后可以再次在代码中使用它,类似于其他变量。使用你定义的函数是如何“运行”它或“调用”它的:
列表 18.3: ex18_demo.py
1 def do_something(): 2 print("I did something!") 3 4 # now we can call it by its name 5 do_something()
当底部的do_something()
运行时,Python 执行以下操作:
- 在 Python 的内存中找到
do_something
函数 - 看到你用
()
调用它 - 跳转到
def do_something()
行的位置 - 运行
def
下的代码行,这里是一行:print("I did
- [rcurvearrowse]
something!"
当def
下的代码完成时,Python 退出函数并跳回到你调用它的位置 - 然后它继续,这在这种情况下是代码的结尾
对于这个练习,你只需要了解一个概念,那就是函数的“参数”:
列表 18.4: ex18_demo.py
1 def do_more_things(a, b): 2 print("A IS", a, "B IS", b) 3 4 do_more_things("hello", 1)
在这种情况下,我有两个参数(也称为“参数”)传递给do_more_things
函数:a
和b
。当我使用do_more_things("hello", 1)
调用这个函数时,Python 临时赋值a="hello"
和b=1
,然后调用函数。这意味着,在函数内部a
和b
将具有这些值,并且当函数退出时它们将消失。这有点像这样:
列表 18.5: ex18_demo.py
1 def do_more_things(a, b): 2 a = "hello" 3 b = 1 4 print("A IS", a, "B IS", b)
请记住这并不完全准确,因为如果你用不同的参数调用do_more_things
,a
和b
会不同。这只是一个例子,一次你用do_more_things ("hello", 1)
调用它。
练习代码
现在花点时间在 Jupyter 中玩耍,创建自己的函数并在尝试这段代码之前调用它们。确保你理解代码如何跳转到函数,然后再跳回来。然后我会让你创建四个不同的函数,然后我会展示它们之间的关系:
列表 18.6: ex18.py
1 # this one is like your scripts with argv 2 def print_two(*args): 3 arg1, arg2 = args 4 print(f"arg1: {arg1}, arg2: {arg2}") 5 6 # ok, that *args is actually pointless, we can just do this 7 def print_two_again(arg1, arg2): 8 print(f"arg1: {arg1}, arg2: {arg2}") 9 10 # this just takes one argument 11 def print_one(arg1): 12 print(f"arg1: {arg1}") 13 14 # this one takes no arguments 15 def print_none(): 16 print("I got nothin'.") 17 18 19 print_two("Zed","Shaw") 20 print_two_again("Zed","Shaw") 21 print_one("First!") 22 print_none()
让我们分解第一个函数print_two
,这与你从制作脚本中已经了解的内容最相似:
- 首先,我们告诉 Python 我们要使用
def
来制作一个函数,其中def
代表“定义”。 - 在与
def
相同的行上,我们给函数命名。在这种情况下,我们只是称之为“print_two”,但也可以是“peanuts”。这不重要,除非你的函数应该有一个简短的名称,说明它的作用。 - 然后我们告诉它我们想要
*args
(星号参数),这很像你的argv
参数,但用于函数。这必须放在()
括号内才能起作用。 - 然后我们以
:
(冒号)结束这一行并开始缩进。 - 冒号之后,所有缩进四个空格的行都将与此名称
print_two
相关联。我们的第一个缩进行是一个解压参数的行,与您的脚本相同。 - 为了演示它的工作原理,我们打印出这些参数,就像在脚本中一样。
print_two
的问题在于这不是制作函数的最简单方法。在 Python 中,我们可以跳过整个解压参数的过程,直接在()
内部使用我们想要的名称。这就是print_two_again
的作用。
在那之后,你有一个在print_one
中接受一个参数的函数示例。
最后,你有一个没有参数的函数print_none
。
警告!
这非常重要。如果现在这还不太明白,不要灰心。我们将做一些练习,将函数与你的脚本链接起来,并向你展示如何制作更多函数。现在,当我说“函数”时,只需继续思考“小型脚本”,并继续尝试。
你应该看到的内容
如果你运行ex18.py
,你应该看到:
1 arg1: Zed, arg2: Shaw 2 arg1: Zed, arg2: Shaw 3 arg1: First! 4 I got nothin'.
立即你就能看到函数是如何工作的。注意你使用函数的方式就像你使用exists
、open
和其他“命令”一样。事实上,我一直在欺骗你,因为在 Python 中,这些“命令”只是函数。这意味着你可以制作自己的命令并在脚本中使用它们。
学习练习
为后续练习创建一个函数清单。将这些检查写在一张索引卡上,并在完成其余练习时或直到你觉得不再需要索引卡时保留在身边:
- 你是否以
def
开始函数定义? - 你的函数名称只包含字符和
_
(下划线)字符吗? - 你是否在函数名称后面放置了
(
(开括号)? - 你是否在
(
(开括号)后面用逗号分隔放置了参数? - 你是否使每个参数都是唯一的(即没有重复的名称)?
- 你是否在参数后面放置了
):
(一个闭括号和一个冒号)? - 你是否将所有想要放在函数中的代码都缩进了四个空格?不多也不少。
- 你是否通过取消缩进(我们称之为“去缩进”)来“结束”函数?
当你运行(“使用”或“调用”)一个函数时,请检查以下事项:
- 你是否通过键入函数名称来调用/使用/运行此函数?
- 你是否在名称后面放置了
(
字符以运行它? - 你是否将想要的值放在括号内,用逗号分隔?
- 你是否以
)
字符结束了函数调用?
在剩下的课程中使用这两个检查表,直到你不再需要它们为止。
最后,对自己重复几次:
1 "To 'run,' 'call,' or 'use' a function all mean the same thing."
常见学生问题
函数名允许使用什么? 与变量名相同。任何不以数字开头且由字母、数字和下划线组成的内容都可以。
在 *args
中的 *
是做什么的? 这告诉 Python 获取函数的所有参数,然后将它们作为列表放入args
中。就像你一直在使用的argv
,但用于函数。除非特别需要,通常不会经常使用。
这感觉真的很无聊和单调。这是好事。这意味着你开始擅长输入代码并理解它的作用。为了让它不那么无聊,把我告诉你要输入的所有内容都故意打破。