IO编程
open()
函数,传入文件名和标示符:
>>> f = open('/Users/michael/test.txt', 'r')
标示符'r'表示读,这样,我们就成功地打开了一个文件。
如果文件不存在,open()
函数就会抛出一个IOError
的错误。
如果文件打开成功,接下来,调用read()
方法可以一次读取文件的全部内容,Python把内容读到内存,用一个str
对象表示:
>>> f.read()
'Hello, world!'
最后一步是调用close()
方法关闭文件。文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的:
>>> f.close()
由于文件读写时都有可能产生IOError
,一旦出错,后面的f.close()
就不会调用。所以,为了保证无论是否出错都能正确地关闭文件,我们可以使用try ... finally
来实现:
try:
f = open('/path/to/file', 'r')
print(f.read())
finally:
if f:
f.close()
但是每次都这么写实在太繁琐,所以,Python引入了with
语句来自动帮我们调用close()
方法:
with open('/path/to/file', 'r') as f:
print(f.read())
这和前面的try ... finally
是一样的。
如果文件过大,可以调用readlines()函数,
for line in f.readlines():
print(line.strip()) # 把末尾的'\n'删掉 通过这样实现
(2)写文件
写文件和读文件是一样的,唯一区别是调用open()
函数时,传入标识符'w'
或者'wb'
表示写文本文件或写二进制文件:
>>> f = open('/Users/michael/test.txt', 'w')
>>> f.write('Hello, world!')
>>> f.close()
你可以反复调用write()
来写入文件,但是务必要调用f.close()
来关闭文件。
还是用with
语句来得保险:
with open('/Users/michael/test.txt', 'w',encoding='gbk') as f:
f.write('Hello, world!')
要写入特定编码的文本文件,请给open()
函数传入encoding
参数,将字符串自动转换成指定编码。
二、StringIO和BytesIO
(1)StringIO顾名思义就是在内存中读写str。要把str写入StringIO,我们需要先创建一个StringIO,然后,像文件一样写入即可:
>>> from io import StringIO
>>> f = StringIO()
>>> f.write('hello')
5
>>> f.write(' ')
1
>>> f.write('world!')
6
>>> print(f.getvalue())
hello world!
getvalue()
方法用于获得写入后的str。
要读取StringIO,可以用一个str初始化StringIO,然后,像读文件一样读取:
>>> from io import StringIO
>>> f = StringIO('Hello!\nHi!\nGoodbye!')
>>> while True:
... s = f.readline()
... if s == '':
... break
... print(s.strip())
...
Hello!
Hi!
Goodbye!
(2)StringIO操作的只能是str,如果要操作二进制数据,就需要使用BytesIO。
dir
、cp
等命令。Python内置的os
模块也可以直接调用操作系统提供的接口函数。
环境变量
在操作系统中定义的环境变量,全部保存在os.environ
这个变量中,可以直接查看:
>>> os.environ
environ({'VERSIONER_PYTHON_PREFER_32_BIT': 'no', 'TERM_PROGRAM_VERSION': '326', 'LOGNAME': 'michael', 'USER': 'michael', 'PATH': '/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/usr/local/mysql/bin', ...})
要获取某个环境变量的值,可以调用os.environ.get('key')
:
>>> os.environ.get('PATH')
'/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/usr/local/mysql/bin'
>>> os.environ.get('x', 'default')
'default'
操作文件和目录
操作文件和目录的函数一部分放在os
模块中,一部分放在os.path
模块中,这一点要注意一下。查看、创建和删除目录可以这么调用:
# 查看当前目录的绝对路径:
>>> os.path.abspath('.')
'/Users/michael'
# 在某个目录下创建一个新目录,首先把新目录的完整路径表示出来:
>>> os.path.join('/Users/michael', 'testdir')
'/Users/michael/testdir'
# 然后创建一个目录:
>>> os.mkdir('/Users/michael/testdir')
# 删掉一个目录:
>>> os.rmdir('/Users/michael/testdir')
把两个路径合成一个时,不要直接拼字符串,而要通过os.path.join()
函数,这样可以正确处理不同操作系统的路径分隔符。在Linux/Unix/Mac下,os.path.join()
返回这样的字符串:
part-1/part-2
而Windows下会返回这样的字符串:
part-1\part-2
(1)序列化可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。
反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。
Python提供了pickle
模块来实现序列化。
首先,我们尝试把一个对象序列化并写入文件:
>>> import pickle
>>> d = dict(name='Bob', age=20, score=88)
>>> pickle.dumps(d)
b'\x80\x03}q\x00(X\x03\x00\x00\x00ageq\x01K\x14X\x05\x00\x00\x00scoreq\x02KXX\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00Bobq\x04u.'
pickle.dumps()
方法把任意对象序列化成一个bytes
,然后,就可以把这个bytes
写入文件。或者用另一个方法pickle.dump()
直接把对象序列化后写入一个file-like Object:
>>> f = open('dump.txt', 'wb')
>>> pickle.dump(d, f)
>>> f.close()
看看写入的dump.txt
文件,一堆乱七八糟的内容,这些都是Python保存的对象内部信息。
当我们要把对象从磁盘读到内存时,可以先把内容读到一个bytes
,然后用pickle.loads()
方法反序列化出对象,也可以直接用pickle.load()
方法从一个file-like Object
中直接反序列化出对象。我们打开另一个Python命令行来反序列化刚才保存的对象:
>>> f = open('dump.txt', 'rb')
>>> d = pickle.load(f)
>>> f.close()
>>> d
{'age': 20, 'score': 88, 'name': 'Bob'}
变量的内容又回来了!
Python的dict
对象可以直接序列化为JSON的{}
,不过,很多时候,我们更喜欢用class
表示对象,比如定义Student
类,然后序列化:
import json
class Student(object):
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
s = Student('Bob', 20, 88)
print(json.dumps(s))
前面的代码之所以无法把Student
类实例序列化为JSON,是因为默认情况下,dumps()
方法不知道如何将Student
实例变为一个JSON的{}
对象。
可选参数default
就是把任意一个对象变成一个可序列为JSON的对象,我们只需要为Student
专门写一个转换函数,再把函数传进去即可:
def student2dict(std):
return {
'name': std.name,
'age': std.age,
'score': std.score
}
这样,Student
实例首先被student2dict()
函数转换成dict
,然后再被顺利序列化为JSON:
>>> print(json.dumps(s, default=student2dict))
{"age": 20, "name": "Bob", "score": 88}
不过,下次如果遇到一个Teacher
类的实例,照样无法序列化为JSON。我们可以偷个懒,把任意class
的实例变为dict
:
print(json.dumps(s, default=lambda obj: obj.__dict__))
如果我们要把JSON反序列化为一个
Student
对象实例,
loads()
方法首先转换出一个
dict
对象,然后,我们传入的
object_hook
函数负责把
dict
转换为
Student,整个程序如下:
class Student(object):
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def __str__(self):
return 'Student object (%s, %s, %s)' % (self.name, self.age, self.score)
def student2dict(std):
return {
'name': std.name,
'age': std.age,
'score': std.score
}
s1 = Student('Bob', 20, 88)
s = student2dict(s1)
json_str = json.dumps(s, default=lambda obj: obj.__dict__)
#使用json序列化协议
rebuild = json.loads(json_str, object_hook=lambda d: Student(d['name'], d['age'], d['score']))
print(rebuild)
print('Python Object translation to json',json_str)
('Python Object translation to json', '{"age": 20, "score": 88, "name": "Bob"}')