从零开始学习PYTHON3讲义(十)自己做一个“电子记事本”
《从零开始python3》第十讲
截至上一讲,我们已经完成了python语言的基本部分。我们用了三讲来讨论python语言的控制结构,用了两讲来介绍python的基本数据类型。可以说仅就语法和语言关键字的部分来讲,当前所学已经足以完成大多数工作。
由本讲开始,我们开始讲述一些经典的python语言应用场景。以案例的形式为引导,学习如何使用python解决具体问题。
我们之前讲过的程序中,所有的操作,都是在内存中进行的。关机或者停电,都会造成内容的丢失。如果想不丢失,就需要把数据保存到硬盘文件中,专有名词称为“持久化”。
硬盘是一种历史遗留的习惯说法,当前的ssd/闪存/存储卡,实际起到的也是同样的作用。
我们编程一直保存的.py文件,就是文件的一种,这是程序的源码实现了持久化,关机、断电都不会丢失。
文件操作
使用python进行文件操作并不难。但是文件这个概念还是很大的,围绕着文件,还有很多概念需要介绍。这些概念具有通用性,并非python所有。
- 文件名:文件必须有一个文件名,通常文件名包含主文件名和扩展文件名(扩展名因为在文件名的最后部分,所以也称为“后缀名”),文件名和扩展名之间使用英文小数点隔开。
不同的操作系统,对于文件名的要求是不同的。通常来说文件名中可以使用字母、数字、下划线,不能使用其它的符号。扩展文件名一般包含特殊的含义,比如.py后缀的文件就代表python语言程序文件。.doc后缀是word文档。
大多数系统都允许中文的文件名,但跟变量名的原因类似,作为程序和程序数据的时候,尽量不选用中文文件名。
- 文件必须有一个存储位置,也就是文件夹,还被称为“目录”。文件夹的名字跟文件有同样的要求,事实上在很多操作系统中,文件夹就是一种特殊的文件。习惯上文件夹不使用扩展名。
- 文件夹是可以包含其它文件和文件夹的。因此从任何一个确定的存储位置开始,可以有“文件夹\文件夹\文件”这样的形式,来精确的定位某一个特定的文件。这称为“path”,中文是“路径”的意思,很形象。(本例中路径所使用的格式是windows的格式,linux等类unix系统使用相反的斜线“/”来间隔文件夹及文件名。) 在同一个文件夹中的文件或者另外的文件夹,必须具有唯一的名字。或者说,路径必须是唯一的,一个路径可以唯一的找到某一个特定的文件。
- 文件路径如果没有指定文件夹部分,只有一个文件名,那代表文件就在“当前目录”。在python中,当前目录指的是程序启动时所在的目录。
好了,以上是关于文件操作的基本知识。下面回到python。
相对内存的操作来说,硬盘、ssd等,虽然速度已经很快,但依然算是计算机中的慢速设备。每次需要进行某个文件操作的时候,python和操作系统,都需要分配系列的资源。包括驱动程序、内存、基础参数等,来支持操作的进行。这个过程叫“打开文件”。
在对文件的操作完成之后,为了让宝贵的系统资源能够被其它程序或者操作使用,需要释放这些资源,这个过程叫做“关闭文件”
在打开文件和关闭文件之间,是文件操作的工作。这样的程序结构在编程中非常常见,也被称为“三明治结构”。以后你还会见到很多这种三明治结构。
python中的文件打开操作使用:
fd=open(文件路径,"文件操作类型")
这是一个打开文件的函数,第一个参数表示要打开文件的文件路径。
第二个参数代表以何种方式操作文件,常用文件操作有:
r : 读取模式,只读取文件,不允许写数据到文件
w : 写入模式,可以读取和写入文件
a : 追加模式,从文件尾部追加数据。
函数打开文件完成后,会返回一个值,在上面代码中是赋值给fd变量。这个值也称为“文件句柄”,这个词算是外来词汇。可以把前面打开文件所申请的内存、驱动程序等资源理解为一口装满水的锅,而句柄则是这个锅的“把手”。有这个句柄在手,就可以通过句柄指挥这些资源对文件做各种操作。所以从这个角度说,句柄这个词翻译的挺传神的。
再提醒一下,文件必须先打开,才能进行其余的文件操作。
python的文件关闭操作要简单的多,原因是不需要提供过多的参数:
fd.close()
文件关闭之后,不能对该文件进行其它操作。
另外你可能注意到了,打开文件的时候,使用的是通用内置函数open。而文件关闭的时候,使用的是“文件句柄”所包含的close()操作,这说明关闭操作,只对句柄这种特定的类型有效。
所以看起来close()的调用没有任何参数,但实际上默认是对fd本身进行了关闭操作。
接着是对文件的读取操作,python有3种常用的读取形式:
#方法1 a = fd.readline() #读一行 #方法2 a= fd.readlines() #读所有行 #方法3 for line in fd: #直接把文件当做一个序列来遍历: print(fd)
使用哪种方式更好,一般看你要做的操作是什么,以及程序的结构方便,功能其实都是差不多的。
最后是写入文件:
fd.write(要写入的内容) #通常写入的内容或者是字符串类型,其它类型要转换成字符串
挑战
今天的挑战就是写一个“记事本”小程序。程序的功能分为三个部分:
- 把内容记录到文件。
- 显示记录的所有内容。
- 删除不再需要的内容。
请先思考一下,可以用伪代码或者流程图描述一下思路,再继续后面的内容。
正式的“记事本”程序实际上很复杂,在手机市场中搜索,能找到上千种app,对于用户体验等方面的设计和功能要求非常高,竞争激烈。我们在这里出于学习目的,并且主要集中在对于文件操作的学习,所以一切都比较简化。
在挑战的题目中,实际上已经把程序分了3部分功能,保存、显示和删除。这等于已经帮助我们进行了整体程序结构的设计。我们沿着这个思路,先使用“伪代码”的形式,把流程梳理清楚。
- 把内容记录到文件
- 获取要记录的内容(笔记内容),这里有一个待解决的问题,就是如何获取?
- 打开文件用于写出
- 保存笔记内容
- 关闭文件
- 显示文件内容
- 打开文件读取
- 逐行读取文件内容
- 显示
- 关闭文件
- 删除不需要的内容
- 首先的问题,如何定位不需要的内容?
- 在显示文件的过程中,对内容按照行进行编号
- 打开文件用于读取
- 全部读取
- 关闭文件
- 打开文件用于写出
- 循环遍历所有行,跳过要删除的行,写出
- 关闭文件
- 首先的问题,如何定位不需要的内容?
- 共性问题
- 三个小程序,都应当读、写同一个文件,否则无法互相配合
逻辑写的并不复杂,我们在下面源码的部分再更细致的讲解。这个“伪代码”提纲的功能,是让你在开始编写程序的时候,不至于不知道如何下手。
既然第4个共性的问题涉及到三个小程序,我们先从这个问题开始解决。方法非常简单,短到只有一行代码:
filename="daily.txt"
这一行代码只是定义了一个字符串变量filename,表示我们使用的记事文件名称。重点在于这行代码如何使用。
程序库
我们的课程一开始就大肆鼓吹python的程序库如何丰富,我们今天就来自己定义一个程序库。上面这个仅仅一行代码的程序,我们保存为common.py,文件名不要输入错,因为我们后面还要用到。
此时common.py就称为一个程序库,虽然看上去很简陋,但它就是程序库。我们在这里很大程度出于演示程序库应用的目的。因为这样简单的功能,并非必须用程序库的方法解决。
现在我们有了一个程序库,使用程序库的方法有三种,我们使用源代码来展示:
#第一种方法 #引用程序库只需要在import之后跟主文件名,不能写上.py后缀 import common a=common.filename #使用其中变量的方法 #第二种 import common as cm #引用库,并改成一个较短的名字 a=cm.filename #第三种 from common import * #引用库中所有的内容,并同当前程序混合 #上面的*符号是所有的意思 a=filename
这三种方法,各有不同的应用场景,可以根据自己的喜好选择。
除了可以定义自己的程序库,python语言已经随着语言本身附带了很多官方发行的程序库,比如用于数学计算的math库。还有很多不同的开发组织提供的,数量庞大的功能库,比如人工智能库tensorflow。后面几讲我们会介绍一些常用扩展库的使用。
今天的挑战至此我们还有一个问题还没有解决办法,就是保存笔记的小程序,如何获取用户输入的记事内容。我们已经学习过了让用户输入的input函数,但启动程序,等待用户输入内容,感觉上啰嗦并且不够友好。
在这里我们尝试一下让用户在执行程序的时候,同时输入一条信息,当做我们程序的参数,随后程序获取这个参数,并记录到记事本中。
这样在程序启动前给定的信息,叫做“命令行参数”,命令行的概念我们课程一开始就讲解过了。获取用户输入的命令行参数,就要用到一个标准的python系统库sys。下面是示例代码:
#引入sys系统库,这个库里包含很多跟操作系统相关的功能 import sys #显示命令行参数的数量和参数内容 print(len(sys.argv),sys.argv) #sys.argv就是命令行的参数,是一个列表 #len(sys.argv)就是列表的长度,可以得到参数的个数 #如果在命令行使用如下命令: python3 args.py 1 2 3 #会得到如下结果: 4 ['args.py', '1', '2', '3']
这里我们学到了第一个系统扩展库sys,第八讲中我们介绍了python内置的帮助函数help。这个函数对于内置的各种库同样有效,比如help(sys)可以列出sys库的详细帮助。此外今天再介绍一个函数dir(),这个函数可以列出某个库中所有可以使用的函数、常量资源。比如你可以在python交互模式中使用dir函数来试试:
>>> import sys #首先要引用sys库,否则下面会报错,找不到sys库 >>> dir(sys) #下一行开始是sys库中可以使用的所有变量和函数,是列表形式 ['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__', '__interactivehook__', '__loader__', '__name__', '__package__', '__spec__', '__stderr__', '__stdin__', '__stdout__', '_clear_type_cache', '_current_frames', '_debugmallocstats', '_framework', '_getframe', '_git', '_home', '_xoptions', 'abiflags', 'api_version', 'argv', 'base_exec_prefix', 'base_prefix', 'breakpointhook', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth', 'get_coroutine_wrapper', 'getallocatedblocks', 'getcheckinterval', 'getdefaultencoding', 'getdlopenflags', 'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info', 'intern', 'is_finalizing', 'last_traceback', 'last_type', 'last_value', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 'set_coroutine_wrapper', 'setcheckinterval', 'setdlopenflags', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'thread_info', 'version', 'version_info', 'warnoptions'] >>>
继续说命令行参数的问题。通常我们都是在idle环境中,使用f5来运行一个程序。因此其实大多数情况,我们的文件保存成了什么名字,都经常不太关心。
如果想获取用户输入的命令行参数,我们当然只能在命令行执行程序,这样才可以输入参数。在命令行执行程序的方式,其实在第一讲演示运行游戏程序的时候已经用过了,那个时候我们没有重点讲解,所以我猜大多数学生应当没记住。
下面是在命令行执行python程序的一般方法,首先要打开命令行程序,这在不同操作系统中方法不同,在windows中是查找cmd命令行图标,点击就可以打开命令行,然后执行python程序的方法:
python3 python程序名.py 参数1 参数2 参数3 ...
开始的python3是通过操作系统的命令行,执行python3解释程序。我们说python是解释型的语言,就是因为我们写的,给电脑看的程序文本文件。需要python3解释程序来翻译,才能被计算机接受、运行。
python3之后是要执行的python程序名,也就是我们自己编写的程序、存盘之后的文件名。再随后是用户输入给程序使用的参数,可以有多个。所有这些各自独立的部分之间,都统一使用空格间隔开。
继续说我们上面的程序,用户输入的命令行参数,会成为一个列表数据。列表的第0个元素,是正在执行的python程序本身,我们的例子中是:args.py。接着就是用户输入的参数了,每个都是一个字符串元素,可以有多个,我们的例子中是3个,加上python程序本身,所以len(sys.argv)得到的是4个参数。如果我们使用for in加上range来遍历的话,刚好可以使用len函数的结果值当做for循环的结束条件。
现在已经可以动手写第一个小程序了:
import sys from common import * #判断是否有且仅有1个参数 if len(sys.argv) != 2: print("参数错误,程序退出!") exit(1) #打开文件追加,第一次没有此文件则自动建立一个空的 fd=open(filename,"a") #写出数据,我们把第1个参数当做记事内容写到文件中 fd.write(sys.argv[1]) #第四讲学过的转义符,\n是换行的意思,我们每写出一条记事独占一行 fd.write("\n") #关闭文件 fd.close()
程序只要执行过之后,你会发现,在程序所在目录(文件夹)下,会多出来一个目录,名称为:
__pycache__
。这个目录是系统自动生成的,目的是加速扩展库的加载和执行,因为这一讲我们使用了自定义的扩展库,虽然很简单,但是也会出现这个文件夹。
我们引用的其它扩展库,也会出现这个文件夹,只是那些库不是我们管理,所以我们看不到。
接着看第二个小程序,显示记事文件内容:
import common #标记一个行数 i = 0 #打开文件读取 fd=open(common.filename,"r") #遍历所有行 for line in fd: #显示行号、内容,end=""表示不换行 #通常end是\n换行符 #不换行的原因是我们在写记事本的时候人为增加了换行\n print(i," ",line,end="") i += 1 fd.close()
最后是第三个小程序,删除记事文件中不要的行:
import sys if len(sys.argv) != 2: print("参数错误,程序退出!") exit(1) linetodelete = int(sys.argv[1]) i = j = 0 fd=open(filename,"r") lines = fd.readlines() fd.close fd=open(filename,"w") for line in lines: if i != linetodelete: print(j," ",line,end="") fd.write(line) j += 1 i += 1 fd.close()
我们来看看程序怎么使用的:
#下面代码块用来演示如何在命令行使用这3个小程序: #首先记录一行记事 d:\dev> python3 dailywrite.py 从零开始python3 #再记录一行 d:\dev> python3 dailywrite.py 寓乐湾教育 #显示记事本内容,注意显示的内容被编号了 d:\dev> python3 dailyread.py 0 从零开始python3 1 寓乐湾教育 #再加入两行记事 d:\dev> python3 dailywrite.py 一行垃圾文字 d:\dev> python3 dailywrite.py python3棒极了 #再显示一次,有4行内容了 d:\dev> python3 dailyread.py 0 从零开始python3 1 寓乐湾教育 2 一行垃圾文字 3 python3棒极了 #删除第2行内容,删除后会显示删除后的内容 d:\dev> python3 dailydelete.py 2 0 从零开始python3 1 寓乐湾教育 2 python3棒极了
练习时间
- 前面删除记事内容的第3个小程序中,变量i/j的功能分别是什么?
- 请为删除记事内容的小程序增加详细注释,除空行外每行都要加注释。
本讲小结
- 文件操作是一个软件的基本操作,用处非常多
- 文件有多种多样的格式,比如音乐、视频、照片、文本
- 程序文件是文本文件,也就是由文字、字符组成的文件,我们的样例“笔记本”程序所记录的文件也是文本文件。
- 文件的操作要小心,以免破坏掉有用的文件
- 扩展库(或:扩展程序库)是python扩展功能的主要形式,python有世界上各个公司、组织发布的海量扩展库资源,在所有的语言中是最多的,python也因此被称为“胶水语言”,意思是把扩展库的功能粘合在一起
- 编程,重要的是由思路。大项目拆成小项目,逐层细化。在这个过程中,我们原来介绍了用函数化来管理这些分拆的每一个部分。今天又学到了程序库,用不同的程序库来分类一组相应的函数或者变量
练习答案
变量i用于对应记事本文件中所有的行,包括将要删除的行。
变量j用于对应删除行之后的文件中所有行。
注释部分略。
上一篇: 去医院体检特划算