简明Python教程·数据结构&解决问题
上一篇专栏


数据结构
数据结构(Data Structures)基本上人如其名——它们只是一种结构,能够将一些数据聚合 在一起。换句话说,它们是用来存储一系列相关数据的集合。
Python 中有四种内置的数据结构——列表(List)、元组(Tuple)、字典(Dictionary)和集合(Set)。我们将了解如何使用它们,并利用它们将我们的编程之路变得更加简单。
列表
列表 是一种用于保存一系列有序项目的集合,也就是说,你可以利用列表保存一串项目的序 列。想象起来也不难,你可以想象你有一张购物清单,上面列出了需要购买的商品,除开在 购物清单上你可能为每件物品都单独列一行,在 Python 中你需要在它们之间多加上一个逗 号。
项目的列表应该用方括号括起来,这样 Python 才能理解到你正在指定一张列表。一旦你创建 了一张列表,你可以添加、移除或搜索列表中的项目。既然我们可以添加或删除项目,我们 会说列表是一种可变的(Mutable)数据类型,意即,这种类型是可以被改变的。
有关对象与类的快速介绍
虽然到目前为止我经常推迟有关对象(Object)与类(Class)的讨论,但现在对它们进行稍 许解释能够有助于你更好地理解列表。我们将在后面的章节讨论有关它们的更多细节。
列表是使用对象与类的实例。当我们启用一个变量 i 并将整数 5 赋值给它时,你可以认为 这是在创建一个 int 类(即类型)之下的对象(即实例) i 。实际上,你可以阅读 help(int) 来了解更多内容。
一个类也可以带有方法(Method),也就是说对这个类定义仅对于它启用某个函数。只有当 你拥有一个属于该类的对象时,你才能使用这些功能。举个例子,Python 为 list 类提供了 一种 append 方法,能够允许你向列表末尾添加一个项目。例如 mylist.append('an item') 将会向列表 mylist 添加一串字符串。在这里要注意到我们通过使用点号的方法来访问对象。
一个类同样也可以具有字段(Field),它是只为该类定义且只为该类所用的变量。只有当你 拥有一个属于该类的对象时,你才能够使用这些变量或名称。字段同样可以通过点号来访 问,例如 mylist.field 。
案例(保存为 ds_using_list.py ):
输出:
它是如何工作的
变量 shoplist 是一张为即将前往市场的某人准备的购物清单。在 shoplist 中,我们只存 储了一些字符串,它们是我们需要购买的物品的名称,但是你可以向列表中添加任何类型的 对象,包括数字,甚至是其它列表。
我们还使用 for...in 循环来遍历列表中的每一个项目。学习到现在,你必须有一种列表也 是一个序列的意识。有关序列的特性将会在稍后的章节予以讨论。
在这里要注意在调用 print 函数时我们使用 end 参数,这样就能通过一个空格来结束输出 工作,而不是通常的换行。
接下来,如我们讨论过的那般,我们通过列表对象中的 append 方法向列表中添加一个对 象。然后,我们将列表简单地传递给 print 函数,整洁且完整地打印出列表内容,以此来检 查项目是否被切实地添加进列表之中。
接着,我们列表的 sort 方法对列表进行排序。在这里要着重理解到这一方法影响到的是列 表本身,而不会返回一个修改过的列表——这与修改字符串的方式并不相同。同时,这也是 我们所说的,列表是可变的(Mutable)而字符串是不可变的(Immutable)。
随后,当我们当我们在市场上买回某件商品时,我们需要从列表中移除它。我们通过使用 del 语句来实现这一需求。在这里,我们将给出我们希望从列表中移除的商品, del 语句 则会为我们从列表中移除对应的项目。我们希望移除列表中的第一个商品,因此我们使用 del shoplise[0] (要记住 Python 从 0 开始计数)。
如果你想了解列表对象定义的所有方法,可以通过 help(list) 来了解更多细节。
元组
元组(Tuple)用于将多个对象保存到一起。你可以将它们近似地看作列表,但是元组不能提 供列表类能够提供给你的广泛的功能。元组的一大特征类似于字符串,它们是不可变的,也 就是说,你不能编辑或更改元组。
元组是通过特别指定项目来定义的,在指定项目时,你可以给它们加上括号,并在括号内部 用逗号进行分隔。
元组通常用于保证某一语句或某一用户定义的函数可以安全地采用一组数值,意即元组内的 数值不会改变。
案例(保存为 ds_using_tuple.py ):
输出:
它是如何工作的
变量 zoo 指的是一个包含项目的元组。我们能够看到 len 函数在此处用来获取元组的长 度。这也表明元组同时也是一个序列。
现在,我们将这些动物从即将关闭的老动物园(Zoo)转移到新的动物园中。因此, new_zoo 这一元组包含了一些本已存在的动物以及从老动物园转移过去的动物。让我们回到话题中 来,在这里要注意到元组中所包含的元组不会失去其所拥有的身份。
如同我们在列表里所做的那般,我们可以通过在方括号中指定项目所处的位置来访问元组中 的各个项目。这种使用方括号的形式被称作索引(Indexing)运算符。我们通过指定 new_zoo[2] 来指定 new_zoo 中的第三个项目,我们也可以通过指定 new_zoo[2][2] 来指定 new_zoo 元组中的第三个项目中的第三个项目 。一旦你习惯了这种语法你就会觉得这其实非常简单。
包含 0 或 1 个项目的元组
一个空的元组由一对圆括号构成,就像 myempty = () 这样。然而,一个只拥有一个项 目的元组并不像这样简单。你必须在第一个(也是唯一一个)项目的后面加上一个逗号 来指定它,如此一来 Python 才可以识别出在这个表达式想表达的究竟是一个元组还是只 是一个被括号所环绕的对象,也就是说,如果你想指定一个包含项目 2 的元组,你必 须指定 singleton = (2, ) 。
针对 Perl 程序员的提示
列表中的列表不会丢失其标识,即列表不会像在 Perl 里那般会被打散(Flattened)。这 同样也适用于元组中的元组、列表中的元组或元组中的列表等等情况。对于 Python 而 言,它们只是用一个对象来存储另一个对象,不过仅此而已。
字典
字典就像一本地址簿,如果你知道了他或她的姓名,你就可以在这里找到其地址或是能够联 系上对方的更多详细信息,换言之,我们将键值(Keys)(即姓名)与值(Values)(即地 址等详细信息)联立到一起。在这里要注意到键值必须是唯一的,正如在现实中面对两个完 全同名的人你没办法找出有关他们的正确信息。
另外要注意的是你只能使用不可变的对象(如字符串)作为字典的键值,但是你可以使用可 变或不可变的对象作为字典中的值。基本上这段话也可以翻译为你只能使用简单对象作为键 值。
在字典中,你可以通过使用符号构成 d = {key : value1 , key2 : value2} 这样的形式,来成 对地指定键值与值。在这里要注意到成对的键值与值之间使用冒号分隔,而每一对键值与值 则使用逗号进行区分,它们全都由一对花括号括起。
另外需要记住,字典中的成对的键值—值配对不会以任何方式进行排序。如果你希望为它们 安排一个特别的次序,只能在使用它们之前自行进行排序。
你将要使用的字典是属于 dict 类下的实例或对象。
案例(保存为 ds_using_dict.py ):
输出:
它是如何工作的
我们通过已经讨论过的符号体系来创建字典 ab 。然后我们通过使用索引运算符来指定某一 键值以访问相应的键值—值配对,有关索引运算符的方法我们已经在列表与元组部分讨论过 了。你可以观察到这之中的语法非常简单。
我们可以通过我们的老朋友—— del 语句——来删除某一键值—值配对。我们只需指定字 典、包含需要删除的键值名称的索引算符,并将其传递给 del 语句。这一操作不需要你知道 与该键值相对应的值。
接着,我们通过使用字典的 item 方法来访问字典中的每一对键值—值配对信息,这一操作 将返回一份包含元组的列表,每一元组中则包含了每一对相应的信息——键值以及其相应的 值。我们检索这一配对,并通过 for...in 循环将每一对配对的信息相应地分配给 name 与 address 变量,并将结果打印在 for 代码块中。
如果想增加一堆新的键值—值配对,我们可以简单地通过使用索引运算符访问一个键值并为 其分配与之相应的值,就像我们在上面的例子中对 Guido 键值所做的那样。
我们可以使用 in 运算符来检查某对键值—值配对是否存在。
要想了解有关 dict 类的更多方法,请参阅 help(dict) 。
关键字参数与字典
如果你曾在你的函数中使用过关键词参数,那么你就已经使用过字典了!你只要这么想 ——你在定义函数时的参数列表时,就指定了相关的键值—值配对。当你在你的函数中 访问某一变量时,它其实就是在访问字典中的某个键值。(在编译器设计的术语中,这 叫作符号表(Symbol Table))
序列
列表、元组和字符串可以看作序列(Sequence)的某种表现形式,可是究竟什么是序列,它 又有什么特别之处?
序列的主要功能是资格测试(Membership Test)(也就是 in 与 not in 表达式)和索引 操作(Indexing Operations),它们能够允许我们直接获取序列中的特定项目。
上面所提到的序列的三种形态——列表、元组与字符串,同样拥有一种切片(Slicing)运算 符,它能够允许我们序列中的某段切片——也就是序列之中的一部分。
案例(保存为 ds_seq.py ):
输出:
它是如何工作的
首先,我们已经了解了如何通过使用索引来获取序列中的各个项目。这也被称作下标操作 (Subscription Operation)。如上所示,每当你在方括号中为序列指定一个数字,Python 将 获取序列中与该位置编号相对应的项目。要记得 Python 从 0 开始计数。因此 shoplist[0] 将获得 shoplist 序列中的第一个项目,而 shoplist[3] 将获得第四个项目。
索引操作也可以使用负数,在这种情况下,位置计数将从队列的末尾开始。因 此, shoplist[-1] 指的是序列的最后一个项目, shoplist[-2] 将获取序列中倒数第二个项目。
你需要通过指定序列名称来进行序列操作,在指定时序列名称后面可以跟一对数字——这是 可选的操作,这一对数字使用方括号括起,并使用冒号分隔。在这里需要注意,它与你至今 为止使用的索引操作显得十分相像。但是你要记住数字是可选的,冒号却不是。
在切片操作中,第一个数字(冒号前面的那位)指的是切片开始的位置,第二个数字(冒号 后面的那位)指的是切片结束的位置。如果第一位数字没有指定,Python 将会从序列的起始 处开始操作。如果第二个数字留空,Python 将会在序列的末尾结束操作。要注意的是切片操 作会在开始处返回 start,并在 end 前面的位置结束工作。也就是说,序列切片将包括起始位 置,但不包括结束位置。
因此, shoplist[1:3] 返回的序列的一组切片将从位置 1 开始,包含位置 2 并在位置 3 时结 束,因此,这块切片返回的是两个项目。类似地, shoplist[:] 返回的是整个序列。
你同样可以在切片操作中使用负数位置。使用负数时位置将从序列末端开始计算。例 如, shoplist[:-1] 强返回一组序列切片,其中不包括序列的最后一项项目,但其它所有项 目都包含其中。
你同样可以在切片操作中提供第三个参数,这一参数将被视为切片的步长(Step)(在默认 情况下,步长大小为 1):
你会注意到当步长为 2 时,我们得到的是第 0、2、4…… 位项目。当步长为 3 时,我们得到 的是第 0、3……位项目。
你可以在 Python 解释器中交互地尝试不同的切片方式的组合,这将帮助你立即看到结果。序 列的一大优点在于你可以使用同样的方式访问元组、列表与字符串。
集合
集合(Set)是简单对象的无序集合(Collection)。当集合中的项目存在与否比起次序或其出 现次数更加重要时,我们就会使用集合。
通过使用集合,你可以测试某些对象的资格或情况,检查它们是否是其它集合的子集,找到 两个集合的交集,等等。
它是如何工作的
这个案例几乎不言自明,因为它涉及的是学校所教授的数学里的基础集合知识。
引用 2
当你创建了一个对象并将其分配给某个变量时,变量只会查阅(Refer)某个对象,并且它也 不会代表对象本身。也就是说,变量名只是指向你计算机内存中存储了相应对象的那一部 分。这叫作将名称绑定(Binding)给那一个对象。
一般来说,你不需要去关心这个,不过由于这一引用操作困难会产生某些微妙的效果,这是 需要你注意的:
案例(保存为 ds_reference.py ):
输出:
它是如何工作的
大部分解释已经在注释中提供。
你要记住如果你希望创建一份诸如序列等复杂对象的副本(而非整数这种简单的对象 (Object)),你必须使用切片操作来制作副本。如果你仅仅是将一个变量名赋予给另一个名 称,那么它们都将“查阅”同一个对象,如果你对此不够小心,那么它将造成麻烦。
针对 Perl 程序员的提示
要记住列表的赋值语句不会创建一份副本。你必须使用切片操作来生成一份序列的副本。
有关字符串的更多内容
在早些时候我们已经详细讨论过了字符串。还有什么可以知道的吗?还真有,想必你还不知 道字符串同样也是一种对象,并且它也具有自己的方法,可以做到检查字符串中的一部分或 是去掉空格等几乎一切事情!
你在程序中使用的所有字符串都是 str 类下的对象。下面的案例将演示这种类之下一些有用 的方法。要想获得这些方法的完成清单,你可以查阅 help(str) 。
案例(保存为 ds_str_methods.py ):
输出:
它是如何工作的
在这里,我们会看见一此操作中包含了好多字符串方法。 startwith 方法用于查找字符串是 否以给定的字符串内容开头。 in 运算符用以检查给定的字符串是否是查询的字符串中的一部分。
find 方法用于定位字符串中给定的子字符串的位置。如果找不到相应的子字符串, find 会返回 -1。 str 类同样还拥有一个简洁的方法用以 联结(Join) 序列中的项目,其中字符串 将会作为每一项目之间的分隔符,并以此生成并返回一串更大的字符串。
总结
我们已经详细探讨了 Python 中内置的多种不同的数据结构。这些数据结构对于编写大小适中 的 Python 程序而言至关重要。
现在我们已经具备了诸多有关 Python 的基本知识,接下来我们将会了解如何设计并编写一款 真实的 Python 程序。
. 第一个“第三个项目”可以是指元组中的元组。 ↩
. 原文作“Reference”,沈洁元译本译作“参考”。此处译名尚存疑,如有更好的翻译建议 还请指出。 ↩

解决问题
我们已经探索了 Python 语言中的许多部分,现在我们将通过设计并编写一款程序来了解如何 把这些部分组合到一起。这些程序一定是能做到一些有用的事情。这其中的方法就是去学习 如何靠你自己来编写一份 Python 脚本。
问题
我们希望解决的问题如下:
我想要一款程序来备份我所有的重要文件。
虽然这是一个简单的问题,但是其中并没有足够的信息有助于让我们开始规划一份解决方 案。我们需要进行一些分析(Analysis)。例如,我们应该如何指定哪些文件是我们需要备份 的?它们应该如何进行备份?储存到哪里?
在正确地分析了这些问题过后,我们便开始设计(Design)我们的程序。我们将列出一份关 于我们的程序应如何运转的清单。在这个案例中,我已经编写了如下清单来说明我将如何工 作。如果由你来设计程序,你可能不会做出同样的分析,因为每个人都有其自己的行事方 式,所以出现不同是完全正常、且正确的。
需要备份的文件与目录应在一份列表中予以指定。
备份必须存储在一个主备份目录中。
备份文件将打包压缩成 zip 文件。
zip 压缩文件的文件名由当前日期与时间构成。
我们使用在任何 GNU/Linux 或 Unix 发行版中都会默认提供的标准 zip 命令进行打包。 在这里你需要了解到只要有命令行界面,你就可以使用任何需要用到的压缩或归档命令。
针对 Windows 用户的提示
Windows 用户可以从 GnuWin32 项目页面 上下载并安装 zip 命令,并将 C:\Program Files\GnuWin32\bin 添加至你的系统的 PATH 环境变量中,这一操作过程与我们为使系统识别 Python 命令本身所做的事情相同。
解决方案
由于我们的程序设计方案现在已经相当稳定,我们便可以开始编写代码,这个过程我们称之 为实现(Implementation)我们的解决方案。
将下述代码保存为 backup_ver1.py :
输出:
现在,我们正处于测试(Testing)阶段,在这一阶段我们测试我们的程序是否能正常工作。 如果其行为不符合我们的预期,那么我们需要对我们的程序进行 Debug 工作,也就是说,移 除程序中的 Bug(错误)。
如果上面的程序不能够正常工作,复制打印在 Zip command is 后面的命令,将其粘贴至 shell(在 GNU/Linux 与 Mac OS X 环境中)或 cmd (对于 Windows 环境),看看存在什么 错误并尝试将其修复。同时你还需要检查 zip 命令手册来看看是不是哪里存在错误。如果这条 命令成功运行,那么可能是错误可能存在在 Python 程序本身之中,因此你需要检查你的程序 是否如上面所展示那番。
它是如何工作的
你会注意到我们是如何一步步将我们的设计转化为代码的。
我们首先导入 os 与 time 模块以准备使用它们。然后,我们在 source 列表中指定我们需 要备份的文件与目录。我们需要存储我们所有备份文件的目标目录在 target_dir 变量中予 以指定。我们将要创建的 zip 归档文件的名字由当前日期与时间构成,在这里通过 time.strftime() 函数来创建。文件名将以 .zip 作为扩展名,并存储在 target_dir 目录 中。
在这里要注意 os.sep 变量的使用方式——它将根据你的操作系统给出相应的分隔符,在 GNU/Linux 与 Unix 中它会是 '/' ,在 Windows 中它会是 '\\' ,在 Mac OS 中它会是 ':' 。使用 os.sep 而非直接使用这些字符有助于使我们的程序变得可移植,从而可以在上 述这些系统中都能正常工作。
time.strftime() 函数会遵循某些格式(Specification),其中一种就如我们在上方程序中所使用的那样。 %Y 将被替换成带有具体世纪的年份。 %m 将会被替换成以 01 至 12 的十进 制数所表示的月份。有关这些格式的全部列表可以在 Python 参考手册中查询到。
我们使用连接(Concatenates)字符串的加法( + )运算符来创建目标 zip 文件的文件名, 也就是说,它将两个字符串连接到一起并返回一个新的字符串。然后,我们创建了一串字符 串 zip_command ,其中包括了我们要执行的命令。如果这条命令不能正常工作,你可以把它 拷贝到 Shell(GNU/Linux 终端或 DOS 提示符)中进行检查。
我们使用的 zip 命令会有一些选项与参数需要传递。 -r 选项用以指定 zip 命令应该递归地 (Recursively)对目录进行工作,也就是说它应该包括所有的子文件夹与其中的文件。这两 个选项结合到一起并可以指定一个快捷方式作 -qr 。选项后面跟着的是将要创建的 zip 文件 的名称,再往后是需要备份的文件与目录的列表。我们通过使用已经讨论过并已了解该如何 运用的的字符串方法 join 来将列表 source 转换成字符串。
随后,我们终于可以运行这一使用了 os.system 函数的命令,这一函数可以使命令像是从系 统中运行的。也就是说,从 shell 中运行的——如果运行成功,它将返回 0 ,如果运行失 败,将返回一个错误代码。
根据命令运行的结果是成功还是失败,我们将打印出与之相应的信息来告诉你备份的结果究 竟如何。
就是这样,我们便创建了一份用以备份我们的重要文件的脚本!
针对 Windows 用户的提示
除了使用双反斜杠转义序列,你还可以使用原始字符串。例如使用 'C:\\Documents' 或 r'C:\Documents' 。然而,不要使用 'C:\Documents' ,因为它将被识别为你使用了一个 未知的转义序列 \D 来结束路径的输入。
现在,我们已经拥有了一份可以正常工作的备份脚本,我们可以在任何我们需要备份文件的 时候使用它。这被称作软件的操作(Operation)或部署(Deployment)阶段。
上面所展示的程序能够正常工作,但是(通常)第一个程序都不会按照你所期望的进行工 作。可能是因为你没有正确地设计程序,或如果你在输入代码时出现了错误。出现这些情况 时,在恰当的时候,你需要回到设计阶段,或者你需要对你的程序进行 Debug 工作。
第二版
我们的第一版脚本已经能够工作了。然而,我们还可以对它作出一些改进,从而使它能够更 好地在每一天都可以正常工作。我们将这一阶段称之为软件的维护(Maintenance)阶段。
我认为有一种颇为有用的改进是起用一种更好的文件命名机制——使用时间作为文件名,存 储在以当前日期为名字的文件夹中,这一文件夹则照常存储在主备份目录下。这种机制的第 一个有点在于你的备份会以分层的形式予以存储,从而使得它们能更易于管理。第二个优点 是文件名能够更短。第三个优点在于由于只有当天进行了备份才会创建相应的目录,独立的 目录能够帮助你快速地检查每天是否都进行了备份。
保存为 backup_ver2.py :
输出:
它是如何工作的
程序的大部分都保持不变。有所改变的部分是我们通过 os.path.exists 函数来检查主文件目 录中是否已经存在了以当前日期作为名称的子目录。如果尚未存在,我们通过 os.mkdir 函 数来创建一个。
第三版
第二版在我要制作多份备份时能够正常工作,但当备份数量过于庞大时,我便很难找出备份 之间有什么区别了。例如,我可能对我的程序或者演示文稿做了重大修改,然后我想将这些 修改与 zip 文件的文件名产生关联。这可以通过将用户提供的注释内容添加到文件名中来实 现。
预先提醒:下面给出的程序将不会正常工作,所以不必惊慌,只需跟着案例去做因为你要在 里面学上一课。
保存为 backup_ver3.py :
输出:
它是如何(不)工作的
这个程序它跑不起来!Python 会说程序之中存在着语法错误,这意味着脚本并未拥有 Python 期望看见的结构。当我们观察 Python 给出的错误时,会看见它同时也告诉我们它检测到错误 的额地方。所以我们开始从那个地方开始对我们的程序进行 Debug 工作。
仔细观察,我们会发现有一独立的逻辑行被分成了两行物理行,但我们并未指定这两行物理 行应该是一起的。基本上,Python 已经发现了该逻辑行中的加法运算符( + )没有任何操作 数,因此它不知道接下来应当如何继续。因此,我们在程序中作出修正。当我们发现程序中 的错误并对其进行修正时,我们称为“错误修复(Bug Fixing)”。
第四版
保存为 backup_ver4.py :
输出:
它是如何工作的
现在程序可以正常工作了!让我们来回顾一下我们在第三版中所作出的实际的增强工作。我 们使用 input 函数来接受用户的注释内容,并通过 len 函数来检查输入内容的长度,以检 查用户是否确实输入了什么内容。如果用户未输入任何内容而直接敲下了 enter 键(也许这 份备份只是一份例行备份而没作出什么特殊的修改),那么我们将继续我们以前所做的工作。
不过,如果用户输入了某些注释内容,那么它将会被附加进 zip 文件的文件名之中,处在 .zip 扩展名之前。在这里需要注意的是我们用下划线替换注释中的空格——这是因为管理 没有空格的文件名总会容易得多。
继续改进
第四版程序已经是一份对大多数用户来说都能令人满意地工作运行的脚本了,不过总会有改 进的余地在。例如,你可以在程序中添加 -v 选项来指定程序的显示信息的详尽 程度,从而 使你的程序可以更具说服力,或者是添加 -q 选项使程序能静默(Quiet)运行。
另一个可以增强的方向是在命令行中允许额外的文件与目录传递到脚本中。我们可以从 sys.argv 列表中获得这些名称,然后我们可以通过 list 类提供的 extend 方法把它们添加 到我们的 source 列表中。
最重要的改进方向是不使用 os.system 方法来创建归档文件,而是使用 zipfile 或 tarfile 内置 的模块来创建它们的归档文件。这些都是标准库的一部分,随时供你在你的电脑上没有 zip 程 序作为没有外部依赖的情况下使用这些功能。
不过,在上面的例子中,我一直都在使用 os.system 这种方式作为创建备份的手段,这样就 能保证案例对于所有人来说都足够简单同时也确实有用。
你可以试试编写第五版脚本吗?在脚本中使用 zipfile 模块而非 os.system 调用。
软件开发流程
我们已经经历了开发一款软件的流程中的各个 阶段(Phases) 。现在可以将这些阶段总结如 下:
What/做什么(分析)
How/怎么做(设计)
Do It/开始做(执行)
Test/测试(测试与修复错误)
Use/使用(操作或开发)
Maintain/维护(改进)
编写程序时推荐的一种方式是遵循我们在编写备份脚本时所经历的步骤:进行分析与设计; 开始实现一个简单版本;测试并修复错误;开始使用以确保工作状况皆如期望那般。现在, 你可以添加任何你所希望拥有的功能,并继续去重复这一“开始做—测试—使用”循环,需要做 多少次就去做多少次。
要记住:
程序是成长起来的,不是搭建出来的。 (Software is grown, not built.) ——Bill de hÓra
总结
我们已经看到了如何创建我们自己的 Python 程序与脚本,也了解了编写这些程序需要经历的 数个阶段。或许你会发现我们在本章中学习的内容对于编写你自己的程序很有帮助,这样你 就能慢慢习惯 Python,同样包括它解决问题的方式。
接下来,我们将讨论面向对象编程。
原文作 Verbosity,沈洁元译本译作“交互”。 ↩

下一篇专栏
