#9 同步IO, os模块,序列化

python IO有同步IO和异步IO,暂且先只讲同步IO,因为异步IO机理比较复杂。

一.文件读写

python中同步IO文件读写,主要涉及以下几个方法:

  • open()
  • read() && read(size)
  • write()
  • readline()
  • readlines()

在谈上面方法使用前,先明确一个概念,叫 file-like object (类文件对象):
在python中把 open() 函数返回的对象,该对象拥有 read() 方法,则称之为file-like object.

读文件

python中使用open方法打开一个文件:

>>> f = open('/Users/james/test.txt', 'r')
# 第1个参数表示文件路径
# 第2个参数表示打开方式(mode),主要有以下几种形式:
  - 'r': 表示 read
  - 'rb': 表示以二进制的形式读,read in bytes
  - 'w': 表示 write
  - 'wb': 表示写入二进制编码, write in bytes
  - 'a': 表示在文件后面追加, append
# 其余还有几种,暂且不谈

如果上面路径中的文件不存在,则python会抛出 IOError 。如果上面的文件成功的读取到,下面可以使用 read() 方法读取其中类容:

>>> f.read()
'Hello, World'

# 千万别忘记使用 'close()' 关闭文件,文件必须关闭
>>> f.close()

由于文件读写可能出错,如果出错,则后面的 close()方法不会被调用,一般使用 'try...finally' 使 'close()' 执行:

try:
  f = open('some.txt', 'r')
  print(f.read())
finally:
  if f:
    f.close()

这样写显得很繁琐,python为我们提供了 with 关键词,用于自动调用close()方法:

with open('some.txt', 'r') as f:
  print(f.read())

值得注意的是,open的路径,如果使用系统路径,则应使用 r 来表示raw字符串,不对路径进行转义

# 如果不在路径前添加 'r' 表示raw,则会报错
>>> f = open(r'I:\front-end\python\some.txt', 'r')

另外,调用read()会一次性读取文件的全部类容,如果内容很大,会出现内存溢出的情况, 我们需要重复调用 read(size) 方法,每次最多读取size这么多内容

readline() 可以很次读取一行的内容, readlines() 一次性读取所有内容,并且按行返回list

二进制文件

下面介绍使用 rb 模式,读取二进制文件,比如图片和视频

>>> f = open('some.jpg', 'rb')
>>> f.read()
b'\xff\xd8\xff\xe1\x00\x18Exif\x00\x00...' # 十六进制表示的字节
>>> f.close()

字符编码

open()方法还可以添加编码格式,默认是utf-8, 使用默认参数 encoding 来表示, 同时使用 errors 参数来表示,如果以该编码打开出错如何处理

>>> f = open('some-gbk.txt', encoding='gbk')
>>> f.read()
'中文测试'

编码错误会抛出 'UnicodeDecodeError' 错误

# errors='ignore' 表示忽略该错误
>>> f = open('some-gbk.txt', encoding='gbk', errors='ignore')

写文件

写文件就是传入open方法中的mode不一样,写可以使用 w 或者 wb

with open('some.txt', 'w') as f:
  f.write('hello, python')

如果该文件不存在,则自动先创建再写

二.StringIO && BytesIO

很多时候,读取的不是文件,而是内存中内容。

StringIO

StringIO表示从内存中读取或者写入str(字符串),首先先引入该模块:

写入:

>>> from io import StringIO
>>> f = StringIO()
>>> f.write('hello')
5
>>> f.write(' ')
1
>>> f.write('world!')
6
>>> print(f.getvalue())
hello world!

使用 getvalue() 方法获取写入后的字符串
另外,可以使用 readline() 来读取该字符串,因为写入字符串之后 stream position 位于最后,直接读会得到空字符串:

>>> f.readline()
''

我们需要将游标指向想要读取的位置, 使用 seek(position) 方法来调整游标位置

>>> f.seek(0)
>>> f.readline()
hello world!

读取
先在内存中初始化字符串再读取:

>>> from io import StringIO
>>> f = StringIO('Hello\nHi\nGreat')
>>> while True:
...   s = f.readline()
...   if s == '':
...     break
...   print(s.strip())

Hello
Hi
Great

BytesIO

这个是操作内存中的二进制数据,和StringIO基本类似:

写入:

>>> from io import BytesIO
>>> f = BytesIO()
>>> f.write('你好'.encode('utf-8')) # 经过utf-8编码的bytes
6
>>> print(f.getvalue())
b'\xe5\x89\xb2\xe8\xae\xa9'

读取:

>>> from io import BytesIO
> >>> f = BytesIO(b'\xe5\x89\xb2\xe8\xae\xa9')
>>> f.read()
b'\xe4\xb8\xad\xe6\x96\x87'

三.os 模块

python提供os模块用于操作目录和文件,函数一部分放在 os 中, 一部分放在 os.path

>>> import os

# 查看os的名称
# linux, unix, mac os x中为 ‘posix’
>>> os.name
'nt'

# 查看环境变量
>>> os.environ
# 一长串

# 获取摸个变量的值
>>> os.environ.get('PATH')

# 查看当前目录的绝对路径
>>> os.path.abspath('.')
'I:\\front-end\\1学习疑问\\python\\基础'

# 在某个目录下创建一个新的目录
# 先把新目录的完整路径表示出来
>>> os.path.join(r'I:\front-end\1学习疑问\python\基础', 'newdir')
'I:\\front-end\\1学习疑问\\python\\基础\\newdir'

# 在创建该目录
>>> os.mkdir('I:\\front-end\\1学习疑问\\python\\基础\\newdir')

# 删除一个目录
>>> os.rmdir('I:\\front-end\\1学习疑问\\python\\基础\\newdir')

os.path.split() 则可以将路径拆分为2部分,后一部分总是最后级别的目录或文件名

>>> os.path.split('I:\\front-end\\1学习疑问\\python\\基础')
('I:\\front-end\\1学习疑问\\python', '基础')

os.path.splitext() 用于获取文件的扩展

>>> os.path.splitext('/path/to/file.txt')
('/path/to/file', '.txt')

列出当前目录下的所有文件目录(os.listdir(), os.path.isdir()):

>>> [x for x in os.listdir('.') is os.path.isdir(x)]
['.lein', '.local', '.m2', '.npm', '.ssh', '.Trash', '.vim', 'Applications', 'Desktop', ...]

列出所有的 '.py' 文件(os.listdir(), os.path.isfile()):

>>> [x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1] == '.py']
['apis.py', 'config.py', 'models.py', 'pymonitor.py', 'test_db.py', 'urls.py', 'wsgiapp.py']

四.序列化

把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫 pickling ,在其他语言中也被称之为serialization。

序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。

反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即 unpickling

json 模块

将python对象变为JSON: json.dumps() (相当于js中 JSON.stringify())

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

另外 dump() 可以直接把JSON写入一个 file-like object

json.loads() 相当于js中的 JSON.parse(),将字符串反序列化:

>>> json_str = '{"name": "james", "age": 20, "score": 10}'
>>> json.loads(json_str)
{'name': 'james', 'age': 20, 'score': 10}

另外 load() 可以从 file-like object 中读取字符串并反序列化

将类class序列化和反序列化

dumps() 方法中有一个 default 参数,可以提供一个转换函数,将类序列化

class Student(object):
  def __init__(self, name, age, score):
    self.name = name
    self.age = age
    self.score =score

>>> s = Student('james', 20, 100)    

# 定义转换函数
def student2dict(std):
  return {
    'name': std.name,
    'age': std.age,
    'score': std.score
  }

# 序列化
>>> print(json.dumps(s, default=student2dict))  
{"name": "james", "age": 20, "score": 100}

这样写如果遇到Teacher类的实例,则无法进行序列化,可以使用class内部属性 __dict__ ,将任意class实例变为dict

# default后面添加lambda表达式
>>> print(json.dumps(s, default=lambda obj: obj.__dict__))
{"name": "james", "age": 20, "score": 100}

反序列化使用 loads() 的一个参数 object_hook

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

>>> json_str = '{"name": "james", "age": 20, "score": 100}'
>>> print(json.loads(json_str, object_hook=dict2student))
<__main__.Student object at 0x00185530>

总结

本章主要将的是python同步IO操作,os模块对路径和文件目录的操作,以及序列化和反序列化,还有class的序列化和反序列化:

  • 同步IO主要涉及: open(), read(), write(), seek() 等方法,根据mode的不同进行不同的操作
  • file-like object 的概念,调用open()方法返回的对象拥有read()方法的对象,称之为类文件对象
  • with 语句,在IO操作中,自动调用 close() 方法
  • StringIOBytesIO 对内存中的字符串和二进制进行读写操作
  • os模块,主要对目录和文件进行操作
  • dumps()loads() 对对象进行序列化和反序列化
  • 使用内部 __dict__ 属性,将对象变为字典,然后进行序列化; object_hook 则将序列化的json字符串反序列化

2017年3月10日 17:29:00

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容