文章目录
一、从文件中读取数据
1.读取整个文件
当前文件和pi_digits.txt在同一层级目录结构下:
with open(r"pi_digits.txt") as f:
contents = f.read()
print(contents)
- 不管要以任何方式使用文件,都得先打开文件,这样才能访问该文件;
- 函数open()接受一个参数:要打开的文件的名称;python在当前执行的文件所在的目录中查找指定的文件;
- 函数open()返回一个表示文件的对象;python将这个对象存储在后面使用的变量 f 中;
- 关键字with在不再需要访问文件后将其关闭;你只管打开文件,并在需要时使用它,python自会在合适的时候将其关闭;
- 使用关键字with时,open()返回的文件对象只能在with代码块内使用;
打开文件的方式:
- r 以只读方式打开文件。文件的指针将会放在文件的开头,这是默认模式;
- rb 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。
- r+ 打开一个文件用于读写。文件指针将会放在文件的开头。
- rb+ 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。
- w 打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
- wb 以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
- w+ 打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
- wb+ 以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
- a 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
- ab 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
- a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
- ab+ 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。
r, w, a
r+, w+,a+
rb, wb, ab
r+b, w+b, a+b
2.只读
r 模式:只读
f = open('pi_digits.txt',mode='r',encoding='utf-8')
contents = f.read()
print(contents)
f.close()
# 输出结果 圆周率:3.1415926535
- open()中第一个参数是要打开文件的名字,第二个参数是要对这个文件进行的操作,第三个参数是用什么编码方式打开文件的内容;
- f 可以写成任意变量,它被称作:文件句柄,文件操作符,或者文件操作对象等;
- open是python调用的操作系统(Windows,Linux等)的功能,Windows的默认编码方式是gbk,Linux默认编码方式是utf-8,所以文件用什么编码保存的,就用什么方法打开,一般都是用utf-8;
- mode为文件打开方式,常见的有r, w, a, r+, w+,a+, rb, wb, ab等,默认不写是r;
- 流程就是打开文件,产生一个文件句柄,对文件句柄进行相应操作,关闭文件;
rb 模式:只读字节
f = open('pi_digits.txt',mode='rb')
contents = f.read()
print(contents)
f.close()
# 输出结果 b'\xe5\x9c\x86\xe5\x91\xa8\xe7\x8e\x87:3.1415926535'
- rb读出来的数据是bytes类型,在rb模式下,不能encoding字符集;
- rb的作用:在读取非文本文件的时候,比如要读取图像,视频等信息的时候就要用到rb,因为这种数据是没办法直接显示出来的;
- rb这个字节的模式是用于传输和存储;
3.路径
相对路径
相对路径就是以当前文件为基准,进行一级级目录指向被引用的的资源文件。
绝对路径
绝对路径就是文件的真正存在的路径,是指从硬盘的根目录开始,进行一级级目录指向文件。
以下是常用的表示当前目录和当前目录的父级目录的标识符
└── mypackage
├── subpackage_1
│ ├── test11.py
│ └── test12.py
└── subpackage_2
├── test21.py
└── test22.py
以test11.py为主文件,. / 表示subpackage_1;. . / 表示mypackage。
- . . / 表示当前文件所在的目录的上一级目录;
- . / 表示当前文件所在的目录(可以省略);
- / 表示当前站点的根目录(域名映射的硬盘目录);
补充__init__.py的作用
(1)标识该目录是一个python的模块包(module package)
如果使用python相关的IDE进行开发,如果目录中存在该文件,该目录就会被识别为 module package
(2)简化模块导入操作
└── mypackage
├── subpackage_1
│ ├── test11.py
│ └── test12.py
├── subpackage_2
│ ├── test21.py
│ └── test22.py
└── subpackage_3
├── test31.py
└── test32.py
- 如果mypackage目录包含了__init__.py时,当用import导入该目录时,会执行__Init__.py里面的代码;
└── mypackage
├── __init__.py
├── subpackage_1
│ ├── test11.py
│ └── test12.py
├── subpackage_2
│ ├── test21.py
│ └── test22.py
└── subpackage_3
├── test31.py
└── test32.py
- mypackage/__init__.py 里面加一个print,如果执行了该文件就会输出:
print("You have imported mypackage")
- 直接用交互模式进行import,显然,__init__.py在包被导入时会被执行。
>>> import mypackage
You have imported mypackage
- 我们可以在__init__.py指定默认需要导入的模块
在mypackage/__init__.py 文件中写入需要导入的模块
from mypackage import subpackage_1,subpackage_2,subpackage_3
等价于
__all__ = ['subpackage_1','subpackage_2','subpackage_3']
- __all__ 关联了一个模块列表,当执行from xx import *(from mypackage import subpackage_1)时,就会导入列表中的模块。
- 此时,子目录中的模块并没有导入!!
└── mypackage
├── __init__.py
├── subpackage_1
│ ├── __init__.py
│ ├── test11.py
│ └── test12.py
├── subpackage_2
│ ├── test21.py
│ └── test22.py
└── subpackage_3
├── test31.py
└── test32.py
- 在subpackage_1 下添加 __init__.py 文件,才可导入子目录下的模块,同样,要想引入subpackage_2 和subpackage_3 下的模块,添加对应的 __init__.py 文件,并写入需要导入的模块
在subpackage_1/__init__.py 文件中写入需要导入的模块
from mypackage.subpackage_1 import test11, test12
等价于
__all__ = ['test11','test12']
4.读操作
read(): 是将文件中所有的内容都读取
with open(r'star.txt',mode='r',encoding='utf-8') as f:
contents = f.read()
print(contents)
# 输出结果:
高圆圆
贾静雯
周芷若
赵敏
- read() 可以指定我们想要读取的内容数量
with open(r'star.txt',mode='r',encoding='utf-8') as f:
contents = f.read(3)
contents2 = f.read()
print(contents)
print(contents2)
# 输出结果:
高圆圆
贾静雯
周芷若
赵敏
- 使用r模式读取的就是文字,使用rb模式读取出来的就是字节
with open(r'star.txt',mode='rb') as f:
contents = f.read(3) # 读取三个字符
contents2 = f.read() # 后面再读就会继续先后读取
print(contents)
print(contents2)
# 输出结果:
b'\xe9\xab\x98'
b'\xe5\x9c\x86\xe5\x9c\x86\r\n\xe8\xb4\xbe\xe9\x9d\x99\xe9\x9b\xaf\r\n\xe5\x91\xa8\xe8\x8a\xb7\xe8\x8b\xa5\r\n\xe8\xb5\xb5\xe6\x95\x8f'
- read()弊端就是当文件很大时,将文件中的内容全部读取,存放在内存中,这样会导致内存崩溃。
readline(): 每次只读取一行
- readline()读取出来的数据,后面都有一个\n
with open(r'star.txt',mode='r',encoding='utf-8') as f:
contents1 = f.readline()
contents2 = f.readline()
contents3 = f.readline()
contents4 = f.readline()
print(contents1)
print(contents2)
print(contents3)
print(contents4)
# 输出结果
高圆圆
贾静雯
周芷若
赵敏
- 内容中间都有一个空行是因为读取的内容后面都有一个\n,print()的时候会将\n执行,解决该问题只需在读取文件后面加一个strip()就可以了
with open(r'star.txt',mode='r',encoding='utf-8') as f:
contents1 = f.readline().strip()
contents2 = f.readline().strip()
contents3 = f.readline().strip()
contents4 = f.readline().strip()
print(contents1)
print(contents2)
print(contents3)
print(contents4)
# 输出结果
高圆圆
贾静雯
周芷若
赵敏
readlines():将每一行形成一个元素,放到一个列表中
- 将所有的内容读取出来,如果文件很大,占内存,容易崩盘;
with open(r'star.txt',mode='r',encoding='utf-8') as f:
contents = f.readlines()
print(contents)
# 输出结果
['高圆圆\n', '贾静雯\n', '周芷若\n', '赵敏']
小结:
- 如果有个较大的文件,在进行读取时,不推荐以下写法:
with open(r'star.txt',mode='r',encoding='utf-8') as f:
print(f.read()) # 这样就是将文件一次性全部读取到内存中,内存容易崩溃
- 推荐下面这种写法:
with open(r'star.txt',mode='r',encoding='utf-8') as f:
for line in f:
print(line) # 这种方式就是在一行一行的进行读取
# print(line.strip())
# 等价于下面实现的功能
with open(r'star.txt',mode='r',encoding='utf-8') as f:
print(f.readline())
print(f.readline())
print(f.readline())
print(f.readline())
- 读完的文件句柄一定要关闭,若使用了关键字with,则不需要考虑关闭文件句柄
- 在读取文本文件时,python将其中的所有文本都解读为字符串 ;如果读取的是数字,并要将其作为数值使用,就必须使用函数int()将其转换为整数,或使用函数float()将其转换为浮点数。
二、写入文件
1.写模式
- 在写文件时,要养成一个写完文件就刷新的习惯,刷新使用flush();
- python只能将字符串写入文本文件。要将数值数据存储到文本文件中,必须先使用函数str()将其转换为字符串格式;
- 函数write()不会在写入的文本末尾添加换行符,因此避免写入多行时内容挤在一起,要让每个字符串单独占一行,需要在write()语句中包含换行符 \n;
覆盖写
with open(r'msg.txt',mode='w',encoding='utf-8') as f:
f.write('789')
f.flush()
- 当选择使用w模式时,在打开文件的时候就会把文件中的内容都清空,然后再操作;
- 注意点:如果文件不存在,使用w模式会创建文件,文件存在,w模式是覆盖写,在打开文件时会把文件中所有的内容清空;
with open(r'msg.txt',mode='wb') as f:
msg = '你好'.encode('utf-8')
f.write(msg)
f.flush()
- wb模式下,不可以指定打开文件的编辑,即encoding;但是写文件的时候必须将字符串转换成utf-8的bytes数据
追加
- 只要是a或者ab,a+ 都是在文件的末尾写入,不论光标在任何位置;
- 在追加模式下,写入的内容会追加的文件的末尾;
- a模式,如果文件不存在就会创建一个新文件;
with open(r'msg.txt',mode='a',encoding='utf-8') as f:
f.write('666')
f.flush()
with open(r'msg.txt',mode='ab') as f:
msg = '再见'.encode('utf-8')
f.write(msg)
f.flush()
2.读写模式
对于读写模式,必须是先读后写,因为光标默认在开头位置,当读完了以后再进行写入。其中,使用频率最高的模式就是r+。
r+模式
msg.txt 中内容:清风不识字,何必乱翻书
(1)正确的操作(先读后写):
with open(r'msg.txt',mode='r+',encoding='utf-8') as f:
contents = f.read()
print(contents) # 清风不识字,何必乱翻书
f.write('你好')
# f.write('\n你好')
f.flush()
with open(r'msg.txt',mode='r',encoding='utf-8') as f2:
ret = f2.read()
print(ret) # 清风不识字,何必乱翻书你好
- 先读后写,正常的读取文件内容后,将要写的内容写在了文件末尾;
- 在先读后写操作完成后,读取msg.txt文件内容为“清风不识字,何必乱翻书你好”,换行需加换行符\n;
(2)错误的操作(先写后读):
with open(r'msg.txt',mode='r+',encoding='utf-8') as f:
f.write('再见')
f.flush()
contents = f.read()
print(contents) # 不识字,何必乱翻书
with open(r'msg.txt',mode='r',encoding='utf-8') as f2:
ret = f2.read()
print(ret) # 再见不识字,何必乱翻书
- 先写后读,把要写的内容“再见”写在了msg.txt的开头,覆盖了原文中的“清风”,且再执行读操作,只能读取msg.txt中“再见”之后的内容,即:“不识字,何必乱翻书”;
- 在先写后读操作完成后,读取msg.txt文件内容为“再见不识字,何必乱翻书”;
注意:
在r+模式下,如果读取了内容,不论读取内容多少,光标显示的是多少,再写入或者操作文件的时候都是在文件的末尾进行的操作。
3.写读模式
w+ 模式
先将所有的内容清空,然后写入,最后读取,但是读取的内容是空的,w+模式不常用。
with open(r'msg.txt',mode='w+',encoding='utf-8') as f:
f.write('你好') # 将文件内容清空,写入“你好”,但后面的读取获取不到数据,为空
f.flush()
contents = f.read()
print(contents) # 结果是空,读不到数据
追加读 a+,a+b
- a+模式下,无论是先读还是后读,都是读取不到数据的
- 还有几个带b的模式,其实就是对字节的一些操作,就不再多赘述
with open(r'msg.txt',mode='a+',encoding='utf-8') as f:
f.write('你好') # 在文件末尾追加“你好”,即:清风不识字,何必乱翻书你好
f.flush()
contents = f.read()
print(contents) # 结果是空,读不到数据
4.修改文件内容
def write_file(filename,contents):
with open(filename,mode='w',encoding='utf=8') as f:
f.write(contents)
f.flush()
def read_file(filename):
with open(filename,mode='r',encoding='utf-8') as f2:
msg = f2.read()
return msg
filename = r'msg.txt'
ret = read_file(filename) # msg.txt中的内容为“清风不识字,何必乱翻书”
new_str = "春风"
old_str = "清风"
result = ret.replace(old_str,new_str)
write_file(filename,result) # msg.txt中的内容修改为“春风不识字,何必乱翻书”
- 先读取文件内容,然后使用replace()方法替换要修改的内容,最后将修改后的内容覆盖写入文件。
5.其他相关操作
seek()
with open(r'msg.txt',mode='r+',encoding='utf-8') as f:
contents = f.read() # 读取文件内容,此时光标移动到末尾
print(contents)
print(f.tell()) # 222
f.seek(0) # 将光标移动到开头
print(f.tell()) # 0
f.seek(0,2) # 将光标移动到结尾
print(f.tell()) # 222
msg = f.read()
print(msg) # 读取文件内容,什么都没有
f.seek(0) # 再次将光标移动到开头
f.write("辛弃疾") # 在开头覆盖写“辛弃疾”,此时光标在9,中文3*3个=9
print(f.tell()) # 9
f.flush()
- seek(n),光标移动到n位置,注意:移动单位是byte,所以如果是utf-8的中文部分要是3的倍数;
- seek(6),只有一个参数并且不是0,表示按照字节来移动光标;
- 通常使用seek()将光标移动到开头或者结尾;
- 移动到开头:seek(0,0),可以看作seek(0);
- 移动到结尾:seek(0,2),第二个参数表示的是从哪个位置进行偏移,默认是0,表示开头;1表示当前位置;2表示结尾。
tell()
- 使用tell()可以获取当前光标所在位置
truncate()
with open(r'msg.txt',mode='w',encoding='utf-8') as f:
f.write("哈哈") # 写入两个字符
f.seek(3) # 光标移动到3,也就是两个字符中间
f.truncate() # 删除光标之后的所有内容
with open(r'msg.txt',mode='r+',encoding='utf-8') as f:
contents = f.read(3) # 读取9个字符
print(contents) # “哈哈哈”
f.seek(4) # 光标移动至4,当truncate()指定截取字符长度,仍从开头进行截取,和光标移动位置无关
f.truncate(9) # 截取9个字符,即:保留开头前9个字符,后面的全删除
- truncate(size)方法用于截取文件,如果指定了可选参数 size,则表示截断文件为 size 个字符。 如果没有指定 size,则从当前位置起截断,后面的所有字符被删除。
- 如果想要做截断操作,要想移动光标,移动到想要截断的位置,然后再进行截断。
三、异常
1.什么是异常
- python使用被称为异常的特殊对象来管理执行期间发生的错误;
- 异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行;
- 一般情况下,在python无法正常处理程序时就会发生一个异常;
- 异常是python对象,表示一个错误;
- 当python脚本发生异常时需要捕获处理它,否则程序会终止执行。
2.异常捕获和处理
当发生异常时,需要对异常进行捕获,然后再进行相应的处理。python的异常捕获常用try/except结构,把可能发生错误的语句放在try模块里,用except来处理异常;每一个try都必须对应至少一个except。此外,与python异常相关的关键字主要有:
关键字 | 关键字说明 |
---|---|
try/except | 捕获异常并处理 |
pass | 忽略异常 |
as | 定义异常实例(except MyError as e) |
else | 如果try中的语句没有引发异常,则执行else中的语句 |
finally | 无论是否出现异常,都执行的代码 |
raise | 抛出/引发异常 |
- 捕捉异常可以使用try/except语句;
- try/except语句用来检测try语句块中的错误,从而让except语句捕捉异常信息并处理;
- 如果不想在异常发生时结束程序,只需在try里捕获它。
(1)捕获所有异常
- except后不带任何异常类型,使用这种方式,不能听过该程序识别出具体的异常信息,因为它捕获了所有的异常;
- 包括键盘中断和程序退出请求(用sys.exit()就无法退出程序了,因为异常被捕获了),因此慎用。
try:
<语句>
except:
print('异常说明')
(2)捕获指定异常
try:
<语句>
except <异常名>:
print('异常说明')
- 万能异常
try:
<语句>
except Exception:
print('异常说明')
- 一个例子
try:
f = open("file-not-exists", "r")
except IOError as e:
print("open exception: %s: %s" %(e.errno, e.strerror))
# 运行结果 open exception: 2: No such file or directory
(3)捕获多个异常
- 捕获多个异常有两种方式,第一种是一个except同时处理多个异常,不区分优先级:
try:
<语句>
except (<异常名1>, <异常名2>, ...):
print('异常说明')
- 第二种是区分优先级的,该异常处理语法的规则是:
- 执行try下的语句,如果引发异常,则执行过程会跳到第一个except语句;
- 如果第一个except中定义的异常与引发的异常匹配,则执行该except中的语句;
- 如果引发的异常不匹配第一个except,则会搜索第二个except,允许编写的except数量没有限制;
- 如果所有的except都不匹配,则异常会传递到下一个调用本代码的最高层的try代码中。
try:
<语句>
except <异常名1>:
print('异常说明1')
except <异常名2>:
print('异常说明2')
except <异常名3>:
print('异常说明3')
(4)异常中的else
- 如果判断完没有某些异常之后还想做其他事情,就可以使用else语句。
try:
<语句>
except <异常名1>:
print('异常说明1')
except <异常名2>:
print('异常说明2')
else:
<语句> # try语句中没有异常则执行此段代码
(5)异常中的finally
- try/finally语句无论是否发生异常都会执行最后的代码。
try:
<语句>
finally:
<语句>
- 一个例子
str1 = 'hello world'
try:
int(str1)
except IndexError as e:
print('IndexError:',e)
except KeyError as e:
print('KeyError:',e)
except ValueError as e:
print('ValueError:',e)
else:
print('try内没有异常')
finally:
print('无论异常与否,都会执行finally')
# 运行结果:
# ValueError: invalid literal for int() with base 10: 'hello world'
# 无论异常与否,都会执行finally
(6)raise主动触发异常
- 可以使用raise语句自己触发异常,raise语法格式如下:
- 语句中Exception是异常的类型(例如ValueError);参数是一个异常参数值,该参数是可选的,如果不提供,异常的参数是“None”;最后一个参数是跟踪异常对象,也是可选的(在实践中很少使用)。
raise [Exception [, args [, traceback]]]
- 一个例子
def not_zero(num):
try:
if num == 0:
raise ValueError('参数错误')
return num
except Exception as e:
print(e)
not_zero(0)
# 运行结果 参数错误
(7)采用traceback模块查看异常
- 发生异常时,python能“记住”引发的异常及程序的当前状态;
- python还维护着traceback(跟踪)对象,其中含有异常发生时与函数调用堆栈有关的信息;
- 记住:异常可能在一系列嵌套较深的函数调用中引发
一个例子
- 如果使用下面的写法,程序只会报“division by zero”错误,但是我们不知道是在哪个文件哪个函数哪一行出的错。
try:
1/0
except Exception as e:
print(e)
# 运行结果 division by zero
- 如果使用traceback模块,就可以帮我们追溯到出错点。
import traceback
try:
1/0
except Exception as e:
traceback.print_exc()
# 运行结果
# Traceback (most recent call last):
# File "c:/Users/dpp/Desktop/csdn/except.py", line 57, in <module>
# 1/0
# ZeroDivisionError: division by zero
- 另外,traceback模块中的traceback.print_exc()跟traceback.format_exc()有什么区别呢?
- format_exc()返回的是字符串,print_exc()则直接给打印出来,即traceback.print_exc()和print(traceback.format_exc())效果是一样的;
- print.exc()还可以接受file参数直接写入到一个文件,比如可以像下面这样把相关信息写入到except.txt文件中去。
traceback.print_exc(file=open('except.txt','w+'))
四、存储数据
- 很多程序都要求用户输入某种信息,如让用户存储游戏首选项或提供要可视化的数据。不管专注的是什么,程序都把用户的信息存储在列表和字典等数据结构中。用户关闭程序时,几乎总是要保存他们提供的信息。
- 如果要将一个系统内的数据通过网络传输给其他系统或客户端,通常需要把这些数据转化为字符串或字节串,而且需要规定一种统一的数据格式才能让数据接收端正确解析并理解这些数据的含义。
- XML是早期被广泛使用的数据交换格式,如今使用更多的数据交换格式是JSON(JavaScript Object Notation),它是一种轻量级的数据交换格式。JSON相对XML而言,更加简单、易于阅读和编写,同时也易于机器解析和生成。
- 序列化:将python内存中的对象存储成一个二进制对象(字节流)。文件是一个字节序列,所以必须把数据转化为字节序列,输出到文件;反之,从文件中恢复到内存就是反序列化,即将二进制转化为可读对象。
1. json模块
- 一种简单的方式是使用模块json来存储数据。
- json模块能够将简单的python数据结构转储到文件中,并在程序再次运行时加载该文件中的数据。
- 可以使用json在python程序之间分享数据,更重要的是,JSON数据格式并非python专有的,可以将以JSON格式存储的数据与使用其他编程语言的人分享。
dump
import json
dic = {'name':'Lucy','age':18}
with open('nums.json','w',encoding='utf-8') as f:
json.dump(dic,f)
- dump()将字典dic存储到文件中,在文件中存储的是字符串类型;
- json.dump(obj,fp),第一个参数是要存储的数据,第二个参数是可用于存储数据的文件对象,就是存到哪个文件中;
- 将obj序列化为JSON格式化流形式的fp;
load
with open('nums.json','r',encoding='utf-8') as f2:
f_dic = json.load(f2)
print(f_dic,type(f_dic)) # {'name': 'Lucy', 'age': 18} <class 'dict'>
print(f_dic.get('name')) # Lucy
- load()将文件中的字符串转成python中的数据类型,并且还能正常使用;
- json.load(fp),参数是一个文件对象,就是从哪个文件中读取数据;
- 将fp反序列化为一个python对象;
dumps
import json
dic = {'name':'Lucy','age':18}
f_dic = json.dumps(dic)
print(f_dic,type(f_dic)) # {"name": "Lucy", "age": 18} <class 'str'>
- dumps()是将python中的数据类型转换成字符串;
- json.dumps(obj),参数是要转换的数据类型;
- 将obj序列化为JSON格式的str;
loads
import json
dic = {'name':'Lucy','age':18}
f_dic = json.dumps(dic)
print(f_dic,type(f_dic)) # {"name": "Lucy", "age": 18} <class 'str'>
d_dic = json.loads(f_dic)
print(d_dic,type(d_dic)) # {'name': 'Lucy', 'age': 18} <class 'dict'>
- loads()将python的数据类型转换成字符串的内容在转成之前的数据类型;
- json.loads(s),参数是要转换成原数据类型的字符串;
- 将s反序列化为python对象。
2. pickle — python对象序列化
- 模块pickle实现了对一个python对象结构的二进制序列化和反序列化。
- “pickling”是将python对象及其所拥有的层次结构转化为一个字节流的过程;
- “unpickling”是相反的操作,会将字节流转化回一个对象层次结构。
dump
import pickle
import time
struct_time = time.localtime()
print(struct_time) # time.struct_time(tm_year=2021, tm_mon=1, tm_mday=29, tm_hour=11, tm_min=38, tm_sec=46, tm_wday=4, tm_yday=29, tm_isdst=0)
with open('nums.json','wb') as f:
pickle.dump(struct_time,f)
- dump()将字典dic存储到文件中,在文件中存储的是字节;
- pickle.dump(obj,file),第一个参数是要存储的数据,第二个参数是可用于存储数据的文件对象,就是存到哪个文件中;
- 将对象obj封存以后的对象写入已打开的file object file;
load
import pickle
import time
struct_time = time.localtime()
print(struct_time) # time.struct_time(tm_year=2021, tm_mon=1, tm_mday=29, tm_hour=11, tm_min=38, tm_sec=46, tm_wday=4, tm_yday=29, tm_isdst=0)
with open('nums.json','rb') as f2:
ret = pickle.load(f2)
print(ret) # time.struct_time(tm_year=2021, tm_mon=1, tm_mday=29, tm_hour=11, tm_min=38, tm_sec=46, tm_wday=4, tm_yday=29, tm_isdst=0)
print(ret.tm_year) # 2021
print(type(ret)) # <class 'time.struct_time'>
- load()将文件中的字节转成python中的数据类型,并且还能正常使用;
- pickle.load(file),参数是一个文件对象,就是从哪个文件中读取数据;
- 从已打开的file object文件中读取封存后的对象,重建其中特定对象的层次结构并返回;
dumps
import pickle
dic = {"name":"Lucy","age":"18"}
str_dic = pickle.dumps(dic)
print(str_dic)
- dumps()是将python中的数据类型转换成字节;
- pickle.dumps(obj),参数是要转换的数据类型;
- 将obj封存以后的对象作为bytes类型直接返回,而不是将其写入到文件;
loads
import pickle
dic = {"name":"Lucy","age":"18"}
str_dic = pickle.dumps(dic)
print(str_dic) # b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x04\x00\x00\x00Lucyq\x02X\x03\x00\x00\x00ageq\x03X\x02\x00\x00\x0018q\x04u.'
print(type(str_dic)) # <class 'bytes'>
dic2 = pickle.loads(str_dic)
print(dic2) # {'name': 'Lucy', 'age': '18'}
print(type(dic2)) # <class 'dict'>
- loads()将python的数据类型转换成字节的内容在转成之前的数据类型;
- pickle.loads(data),参数是要转换成原数据类型的字节;
- 重建并返回一个对象的封存表示形式data的对象层级结构。data必须为bytes-like object。
3. json和pickle对比
- json是一个文本序列化格式(输出Unicode文本,尽管在大多数时候它会接着以utf-8编码),而pickle是一个二进制序列化格式;
- json是可以直接阅读的,而pickle不是;
- json是可互操作的,在python系统之外广泛使用,而pickle则是python专用的;
- 默认情况下,json只能表示python内置类型的子集,不能表示自定义的类;但pickle可以表示大量的python数据类型;pickle能够将函数名进行序列化但是json不行,pickle不能序列化匿名函数;
- 不像pickle,对一个不信任的json进行反序列化的操作本身不会造成任意代码执行漏洞。pickle模块并不安全,只应对信任的数据进行unpickle操作(构建恶意的pickle数据,在执行unpickle时执行任意代码是可能的);在处理不信任数据时,json可能更为合适。
- 使用推荐:如果序列化的内容是列表或字典,推荐使用json模块;序列化其他的数据类型,之后又需要进行反序列化,可以使用pickle。
总结
- 资源 – 文件和异常.emmx