Python学习笔记8

Python学习笔记–8.I/O

2015-07-21

MrZigZag @ Peking University

1.文件

Python内置读写文件的函数,用法与C类似。读写文件一般都是请求操作系统打开一个文件对象,然后通过操作系统的借口从这个文件对象读写数据。

读文件,使用内置的open()函数,传入文件名和读写标识符。

f = open('file_name', 'r')

调用read()方法可以一次性读取全部内容存放到一个str中,最后调用close()关闭文件。文件使用完毕之后必须关闭。每次都写close()并不方便,Python提供了with语句自动调用close()

with open('file_name', 'r') as f:
    str = f.read()

与Java类似,Python要求读文件时要调用try-except机制来处理可能会出现的问题。

try:
    f = open('file_name', 'r')
    str = f.read()
finally:
    if f:
        f.close()

读文件还可以使用read(size)每次读取size字节的内容,或者用readline()读取一行,还可以用readlines()一次读取所有行并生成一个list。

读二进制文件,用rb模式打开文件即可。

with open(file_name, 'rb') as f:
    u_str = f.read()

一般而言,读取非Ascii编码的文本文件都是先用二进制打开,读取再解码。Python提供codecs模块处理读文件时的自动编码。

import codecs
with codecs.open(file_name, 'r', 'gbk') as f:
    f.read()

写文件,与读文件类似,区别在于用了write(),使用标识符w或者wb。同时可使用with。要写入特定编码的文件,可以使用codecs

2.操作文件和目录

Python内置了os模块,可以直接调用操作系统提供的接口函数。例如:

>>> import os
>>> os.name
'posix'

>>> os.uname() #操作系统的详细信息
('Darwin', 'MrZigZagdeMacBook-Pro.local', '14.4.0', 'Darwin Kernel Version 14.4.0: Thu May 28 11:35:04 PDT 2015; root:xnu-2782.30.5~1/RELEASE_X86_64', 'x86_64')

>>> os.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.getenv('PATH') #获取环境变量的值
'/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/texbin:/Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk/Contents/Home/bin'

操作文件和目录的函数放在osos.path模块中。

>>> os.path.abspath('.') # 当前目录的绝对路径
'/Users/mrzigzag'

将两个路径组合时,根据操作系统的差异性,一般不要直接拼接字符串,最好用os.path.join()函数,这样可以处理不同操作系统的路径分隔符。

拆分路径时用os.path.split()可以将路径拆分成 两个部分,最后一个部分总是最后级别的目录或者文件名。得到文件扩展名os.path.splitext()

创建目录os.mkdir(dir),删除目录os.rmdir(dir)

>>> os.path.join('/Users/mrzigzag', 'testdir')
'/Users/mrzigzag/testdir'
>>> os.mkdir('/Users/mrzigzag/testdir')
>>> os.rmdir('/Users/mrzigzag/testdir')
>>> os.path.split('/Users/mrzigzag/testdir')
('/Users/mrzigzag', 'testdir')

重命名os.rename(name.old, name.new),删除os.remove(file)

复制文件需要shutil模块提供的copyfile()函数。

利用Python的特性对文件目录进行批量操作,例如列出所有文件。

>>> [x for x in os.listdir('.') if not os.path.isdir(x)]

要列出所有.jpg文件。

>>> [x for x in os.listdir('.') if x not os.path.isdir(x) and os.path.splitext(x)[1] == '.jpg']

3.序列化

把变量从内存中变成可存储或者可传输的过程称为序列化。反之称为反序列化。

Python提供两个模块来实现序列化,cPicklepickle,功能一样,但是cPickle用c写,速度快,一般优先调用。

try:
    import cPickle as pickle
except ImportError:
    import pickle

将对象写到文件。可以用pickle.dumps()方法将任意对象序列化成为一个str,然后将该str写入到文件。或者用pickle.dump()直接将对象序列化后写入到一个file-like Object。

with open('file_name', 'wb') as f:
    pickle.dump(d, f)

读入同理,pickle.loads()是从字符串读入,pickle.load()则是从file-like Object直接读入。

Pickle的问题主要是保存的格式只适合Python,不同版本的Python可能都不兼容。如果有更加通用的序列化需求,用json格式才是一个比较好的选择。

JSON是标准的Javascript对象,表示出来就是一个字符串,可以被所有语言读取,也方便存储和传输。

Python内置了json模块提供了完善的Python对象到JSON格式的转换,内置的序列化函数和反序列化函数与pickle类似。

>>> import json
>>> d = dict(name='Bob', age=20, score=88)
>>> json.dumps(d)
'{"age": 20, "score": 88, "name": "Bob"}'

需要注意的一点是反序列化得到的所有字符串对象都是unicode而不是str,因为JSON标准规定的编码是UTF-8。

json模块支持直接将Python的基本数据类型转换到JSON,对于自定义的类则需要用转换函数进行转换。

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))

运行会得到一个TypeError,因为Student不是一个可序列化为JSON的对象。利用可选参数default,写一个转换函数即可。

def studetn2dict(std):
    return {
        'name': std.name,
        'age': std.age,
        'score': std.score
    }

print(json.dumps(s, default=student2dict))

遇到其他子类的实例时可能会无法转换,因而可以直接利用class中得__dict__属性,它就是一个用来存储实例变量的dict

print(json.dumps(s, default=lambda obj: ojb.__dict__))

反序列化则是先用json.loads()将JSON转换成一个dict,然后传入object_hook将dict转换成具体的实例。

def dict2student(d):
    return Student(d['name'], d['age'], d['score])

json_str = '{"age": 20, "score": 88, "name": "Bob"}'
print(json.loads(json_str, object_hook=dict2student))

关于json.dumps()方法提供的可选参数,可以参考这里