读取和写入
要写入一个文件,你必须在打开文件时设置第二个参数来为 'w' 模式:
>>> fout = open('output.txt', 'w')
如果该文件已经存在,那么用写入模式打开它将会清空原来的数据并从新开始,所以要小心! 如果文件不存在,那么将创建一个新的文件。
open会返回一个文件对象,该对象提供了操作文件的方法。write 方法将数据写入文件。
>>> line1 = "This here's the wattle,\n"
>>> fout.write(line1)
24
返回值是被写入字符的个数。文件对象将跟踪自身的位置,所以下次你调用 write 的时候,它会在文件末尾添加新的数据。
>>> line2 = "the emblem of our land.\n"
>>> fout.write(line2)
24
完成文件写入后,你应该关闭文件。
>>> fout.close()
如果你不关闭这个文件,程序结束时它才会关闭。
格式化运算符
write的参数必须是字符串,所以如果想要在文件中写入其它值, 我们需要先将它们转换为字符串。最简单的法是使用 str :
>>> x = 52
>>> fout.write(str(x))
另一个方法是使用 格式化运算符(format operator) ,即 %。 作用于整数时,% 是取模运算符,而当第一个运算数是字符串时,% 则是格式化运算符。
第一个运算数是 格式化字符串(format string) ,它包含一个或多个 格式化序列(format sequence) 。格式化序列指定了第二个运算数是如何格式化的。运算结果是一个字符串。
例如,格式化序列 '%d' 意味着第二个运算数应该被格式化为一个十进制整数:
>>> camels = 42
>>> '%d' % camels
'42'
结果是字符串 '42' ,需要和整数值 42 区分开来。
一个格式化序列可以出现在字符串中的任何位置,所以可以将一个值嵌入到一个语句中:
>>> 'I have spotted %d camels.' % camels
'I have spotted 42 camels.'
如果字符串中有多个格式化序列,那么第二个参数必须是一个元组。 每个格式化序列按顺序和元组中的元素对应。
下面的例子中使用 '%d' 来格式化一个整数, '%g' 来格式化一个浮点数,以及 '%s' 来格式化一个字符串:
>>> 'In %d years I have spotted %g %s.' % (3, 0.1, 'camels')
'In 3 years I have spotted 0.1 camels.'
元组中元素的个数必须等于字符串中格式化序列的个数。 同时,元素的类型也必须符合对应的格式化序列:
>>> '%d %d %d' % (1, 2)
TypeError: not enough arguments for format string
>>> '%d' % 'dollars'
TypeError: %d format: a number is required, not str
在第一个例子中,元组中没有足够的元素;在第二个例子中,元素的类型错误。
文件名和路径
文件以 **目录(directory)** (也称为“文件夹(folder)”)的形式组织起来。 每个正在运行的程序都有一个“当前目录(current directory)”作为大多数操作的默认目录。 例如,当你打开一个文件来读取时,Python 会在当前目录下寻找这个文件。
`os`模块提供了操作文件和目录的函数(“os”代表“operating system”)。`os.getcwd` 返回当前目录的名称:
>>> import os
>>> cwd = os.getcwd()
>>> cwd
'/home/dinsdale'
</pre>
`cwd`代表“current working directory”,即“当前工作目录”。 在本例中,返回的结果是 `/home/dinsdale` ,即用户名为 `dinsdale` 的主目录。
类似 `'/home/dinsdale'` 这样的字符串指明一个文件或者目录,叫做 **路径(path)** 。
一个简单的文件名,如 `memo.txt` ,同样被看做是一个路径,只不过是 **相对路径(relative path)** ,因为它是相对于当前目录而言的。如果当前目录是 `/home/dinsdale` ,那么文件名 `memo.txt` 就代表 `/home/dinsdale/memo.txt` 。
一个以 `/` 开头的路径和当前目录无关,叫做 **绝对路径(absolute path)**。要获得一个文件的绝对路径,你可以使用 `os.path.abspath` :
>>> os.path.abspath('memo.txt')
'/home/dinsdale/memo.txt'
`os.path`还提供了其它函数来对文件名和路径进行操作。例如,`os.path.exists` 检查一个文件或者目录是否存在:
>>> os.path.exists('memo.txt')
True
如果存在,可以通过 `os.path.isdir` 检查它是否是一个目录:
>>> os.path.isdir('memo.txt')
False
>>> os.path.isdir('/home/dinsdale')
True
类似的,`os.path.isfile` 检查它是否是一个文件。
`os.listdir`返回给定目录下的文件列表(以及其它目录)。
>>> os.listdir(cwd)
['music', 'photos', 'memo.txt']
接下来演示下以上函数的使用。下面的例子“遍历”一个目录,打印所有文件的名字,并且针对其中所有的目录递归的调用自身。
def walk(dirname):
for name in os.listdir(dirname):
path = os.path.join(dirname, name)
if os.path.isfile(path):
print(path)
else:
walk(path)
`os.path.join`接受一个目录和一个文件名,并把它们合并成一个完整的路径。
os模块提供了一个叫做 `walk` 的函数,和我们上面写的类似,但是功能更加更富。
## 捕获异常
试图读写文件时,很多地方可能会发生错误。如果你试图打开一个不存在的文件夹, 会得到一个输入输出错误(IOError):
>>> fin = open('bad_file')
IOError: [Errno 2] No such file or directory: 'bad_file'
如果你没有权限访问一个文件:
>>> fout = open('/etc/passwd', 'w')
PermissionError: [Errno 13] Permission denied: '/etc/passwd'
如果你试图打开一个目录来读取,你会得到:
>>> fin = open('/home')
IsADirectoryError: [Errno 21] Is a directory: '/home'
为了避免这些错误,你可以使用类似 `os.path.exists` 和 `os.path.isfile` 的函数来检查,但这将会耗费大量的时间和代码去检查所有的可能性(从“Errno 21”这个错误信息来看,至少有21种可能出错的情况)。
更好的办法是在问题出现的时候才去处理,而这正是 `try` 语句做的事情。 它的语法类似 `if...else` 语句:
try:
fin = open('bad_file')
except:
print('Something went wrong.')
Python 从 `try` 子句(clause)开始执行。 如果一切正常,那么 `except` 子句将被跳过。 如果发生异常,则跳出 `try` 子句,执行 `except` 子句。
使用 `try` 语句处理异常被称为是 **捕获(catching)** 异常。 在本例中,`except` 子句打印出一个并非很有帮助的错误信息。 一般来说,捕获异常后你可以选择是否解决这个问题,或者继续尝试运行,又或者至少优雅地结束程序。
数据库
数据库是一个用来存储数据的文件。 大多数的数据库采用类似字典的形式,即将键映射到值。 数据库和字典的最大区别是,数据库是存储在硬盘上(或者其他永久存储中), 所以即使程序结束,它们依然存在。
dbm模块提供了一个创建和更新数据库文件的接口。
打开数据库和打开其它文件的方法类似:
>>> import dbm
>>> db = dbm.open('captions', 'c')
模式 'c' 代表如果数据库不存在则创建该数据库。 这个操作返回的是一个数据库对象,可以像字典一样使用它(对于大多数操作)。
当你创建一个新项时,dbm 将更新数据库文件。
>>> db['cleese.png'] = 'Photo of John Cleese.'
当你访问某个项时,dbm 将读取文件:
>>> db['cleese.png']
b'Photo of John Cleese.'
返回的结果是一个 字节对象(bytes object) ,这就是为什么结果以 b 开头。 一个字节对象在很多方面都和一个字符串很像。但是当你深入了解 Python 时, 它们之间的差别会变得很重要,但是目前我们可以忽略掉那些差别。
如果你对已有的键再次进行赋值,dbm 将把旧的值替换掉:
>>> db['cleese.png'] = 'Photo of John Cleese doing a silly walk.'
>>> db['cleese.png']
b'Photo of John Cleese doing a silly walk.'
一些字典方法,例如 keys 和 items ,不适用于数据库对象,但是 for 循环依然适用:
for key in db:
print(key, db[key])
与其它文件一样,当你完成操作后需要关闭文件:
>>> db.close()
序列化
dbm 的一个限制在于键和值必须是字符串或者字节。 如果你尝试去用其它数据类型,你会得到一个错误。
pickle模块可以解决这个问题。它能将几乎所有类型的对象转化为适合在数据库中存储的字符串,以及将那些字符串还原为原来的对象。
pickle.dumps读取一个对象作为参数,并返回一个字符串表示(dumps 是“dump string”的缩写):
>>> import pickle
>>> t = [1, 2, 3]
>>> pickle.dumps(t)
b'\x80\x03]q\x00(K\x01K\x02K\x03e.'
这个格式对人类来说不是很直观,但是对 pickle 来说很容易去解释。pickle.loads (“load string”)可以重建对象:
>>> t1 = [1, 2, 3]
>>> s = pickle.dumps(t1)
>>> t2 = pickle.loads(s)
>>> t2
[1, 2, 3]
尽管新对象和旧对象有相同的值,但它们(一般来说)不是同一个对象:
>>> t1 == t2
True
>>> t1 is t2
False
换言之,序列化然后反序列化等效于复制一个对象。
你可以使用 pickle 将非字符串对象存储在数据库中。 事实上,这个组合非常常用,已经被封装进了模块 shelve 中。
管道
任何可以在shell中启动的程序,也可以在 Python 中通过使用 管道对象(pipe object) 来启动。一个管道代表着一个正在运行的程序。
例如,Unix 命令 ls -l 将以详细格式显示当前目录下的内容。 你可以使用 os.popen 来启动 ls :
>>> cmd = 'ls -l'
>>> fp = os.popen(cmd)
实参是一个包含shell命令的字符串。返回值是一个行为类似已打开文件的对象。 你可以使用 readline 来每次从 ls 进程的输出中读取一行,或者使用 read 来一次读取所有内容:
>>> res = fp.read()
当你完成操作后,像关闭一个文件一样关闭管道:
>>> stat = fp.close()
>>> print(stat)
None
返回值是 ls 进程的最终状态。None 表示正常结束(没有出现错误)。
例如,大多数 Unix 系统提供了一个叫做 md5sum 的命令,来读取一个文件的内容并计算出一个“校验和(checksum)”。
你可以使用一个管道来从 Python 中运行 md5sum ,并得到计算结果:
>>> filename = 'book.tex'
>>> cmd = 'md5sum ' + filename
>>> fp = os.popen(cmd)
>>> res = fp.read()
>>> stat = fp.close()
>>> print(res)
1e0033f0ed0656636de0d75144ba32e0 book.tex
>>> print(stat)
None
编写模块
任何包含 Python 代码的文件,都可以作为模块被导入。
作为模块的程序通常写成以下结构:
if __name__ == '__main__':
print(linecount('wc.py'))
__name__是一个在程序开始时设置好的内建变量。 如果程序以脚本的形式运行,__name__ 的值为 __main__ ,这时其中的代码将被执行。否则当被作为模块导入时,其中的代码将被跳过。
我们做个练习,将例子输入到文件 wc.py 中,然后以脚本形式运行它。 接着,打开 Python 解释器并导入 wc 。当模块被导入后, __name__ 的值是什么?
警示:如果你导入一个已经被导入了的模块,Python 将不会做任何事情。它并不会重新读取文件,即使文件的内容已经发生了改变。
如果你要重载一个模块,可以使用内建函数 reload ,但它可能会出错。因此最安全的方法是重启解释器,然后重新导入模块。
内置变量
name
wc.py
def lineCount(filename):
count = 0
for line in open(filename):
count += 1
return count
# print(lineCount('wc.py'))
if __name__ == '__main__':
print(lineCount('wc.py'))
wc1.py
import wc
wc.lineCount('wc1.py')
print("hello world\n")
网友评论