> 백엔드 개발 > 파이썬 튜토리얼 > Python 데이터 직렬화(json, pickle, shelve)에 대한 자세한 설명

Python 데이터 직렬화(json, pickle, shelve)에 대한 자세한 설명

Y2J
풀어 주다: 2017-04-20 09:58:05
원래의
7290명이 탐색했습니다.

이 글에서는 주로 Python의 데이터 직렬화를 소개합니다. 이 섹션에서는 데이터 직렬화를 위해 Python에 내장된 여러 모듈을 소개합니다.

1. 서문

1. 진짜 필요

모든 각 프로그래밍 언어에는 고유한 데이터 유형이 있습니다. 객체 지향 프로그래밍 언어를 사용하면 개발자가 데이터 유형(예: 사용자 정의 클래스)을 사용자 정의할 수 있으며 Python에서도 마찬가지입니다. 우리는 종종 다음과 같은 요구 사항을 가지고 있습니다.

  1. 메모리에 있는 다양한 데이터 유형을 네트워크를 통해 다른 시스템이나 클라이언트로 전송합니다.

  2. 다양한 데이터를 저장합니다. 지속성을 위해 메모리를 로컬 디스크에 입력합니다.

2. 데이터 형식

네트워크를 통해 한 시스템에서 다른 시스템이나 클라이언트로 전송하려면 일반적으로 먼저 데이터를 문자열이나 바이트 문자열로 변환해야 하며, 데이터 수신 측에서 이러한 데이터의 의미를 올바르게 구문 분석하고 이해할 수 있도록 통일된 데이터 형식을 지정해야 합니다. XML은 초기에 널리 사용되었던 데이터 교환 형식으로, 초기 시스템 통합 논문에서 자주 볼 수 있습니다. 요즘에는 경량 데이터 교환 형식인 JSON(Javascript Object Notation)이 더 자주 사용됩니다. . XML에 비해 JSON은 더 간단하고, 읽고 쓰기 쉽고, 기계에서 구문 분석하고 생성하기가 더 쉽습니다. 이 외에도 내부적으로 사용되는 데이터 교환 형식을 사용자 정의할 수도 있습니다.

데이터를 로컬 디스크에 유지하려는 경우 데이터의 이 부분은 일반적으로 시스템 내부에서만 사용됩니다. 따라서 데이터 변환 프로토콜과 변환된 데이터 형식이 표준일 필요는 없습니다. 시스템 내부적으로만 정확하게 식별하면 됩니다. 그러나 시스템 내의 변환 프로토콜은 일반적으로 프로그래밍 언어 버전의 업그레이드(알고리즘 개선, 효율성 향상)에 따라 변경되므로 일반적으로 변환 프로토콜과 프로그래밍 언어 버전 간의 호환성 문제가 관련됩니다. 다음 피클 프로토콜은 다음과 같습니다. 한 가지 예.

3. 직렬화/역직렬화

객체를 네트워크를 통해 전송하거나 로컬 디스크에 저장할 수 있는 데이터 형식으로 변환합니다(예: 예: XML, JSON 또는 특정 형식의 바이트 문자열)을 직렬화라고 합니다. 그렇지 않으면 역직렬화라고 합니다.

4. 관련 모듈

이 섹션에서는 데이터 직렬화를 위해 Python에 내장된 여러 모듈을 소개합니다.

模块名称 描述 提供的api
json 用于实现Python数据类型与通用(json)字符串之间的转换 dumps()、dump()、loads()、load()
pickle 用于实现Python数据类型与Python特定二进制格式之间的转换 dumps()、dump()、loads()、load()
shelve 专门用于将Python数据类型的持久化到磁盘,shelf是一个类似dict的对象,操作十分便捷 open()

2.json 모듈

대부분의 프로그래밍 언어는 json 데이터 처리를 위한 인터페이스를 제공하며, Python 2.6에는 json 모듈이 추가되었습니다. 내장 모듈, 다운로드가 필요하지 않습니다.

1. 직렬화 및 역직렬화

Python의 JSON 모듈의 직렬화 및 역직렬화 프로세스를 각각 인코딩 및 디코딩이라고 합니다.

  1. 인코딩: Python 객체를 JSON 문자열로 변환

  2. 디코딩: JSON 문자열을 Python 객체로 변환

json 모듈은 직렬화 및 역직렬화 작업을 위해 다음 두 가지 메서드를 제공합니다.



# 序列化:将Python对象转换成json字符串
dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)

# 反序列化:将json字符串转换成Python对象
loads(s, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
로그인 후 복사

또한 json 모듈에서는 다음과 같은 두 가지 추가 메서드도 제공됩니다. 직렬화된 json 데이터를 파일에 직접 저장하고, 파일에 있는 json 데이터를 직접 읽어 역직렬화하는 방법:



# 序列化:将Python对象转换成json字符串并存储到文件中
dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, indent=None, separators=None, default=None, sort_keys=False, **kw)

# 反序列化:读取指定文件中的json字符串并转换成Python对象
load(fp, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
로그인 후 복사

2. JSON과 Python 간의 데이터 유형 대응

Python과 JSON

JSON을 Python으로

JSONPython
objectdict
arraylist
stringstr
number(int)int
number(real)float
trueTrue
falseFalse
nullNone

说明:

  1. Python dict中的非字符串key被转换成JSON字符串时都会被转换为小写字符串;

  2. Python中的tuple,在序列化时会被转换为array,但是反序列化时,array会被转化为list;

  3. 由以上两点可知,当Python对象中包含tuple数据或者包含dict,且dict中存在非字符串的key时,反序列化后得到的结果与原来的Python对象是不一致的;

  4. 对于Python内置的数据类型(如:str, unicode, int, float, bool, None, list, tuple, dict)json模块可以直接进行序列化/反序列化处理;对于自定义类的对象进行序列化和反序列化时,需要我们自己定义一个方法来完成定义object和dict之间进行转化。

3. 实例:内置数据类型序列化/反序列化

序列化


# 序列化
>>> json.dumps({'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)})
'{"a": "str", "c": true, "b": 11.1, "e": 10, "d": null, "g": [4, 5, 6], "f": [1, 2, 3]}'
로그인 후 복사

sort_keys参数: 表示序列化时是否对dict的key进行排序(dict默认是无序的)


# 序列化并对key进行排序
>>> json.dumps({'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}, sort_keys=True)
'{"a": "str", "b": 11.1, "c": true, "d": null, "e": 10, "f": [1, 2, 3], "g": [4, 5, 6]}'
로그인 후 복사

indent参数: 表示缩进的意思,它可以使得数据存储的格式变得更加优雅、可读性更强;如果indent是一个非负整数或字符串,则JSON array元素和object成员将会被以相应的缩进级别进行打印输出;如果indent是0或负数或空字符串,则将只会插入换行,不会有缩进。


# 序列化并对key进行排序及格式化输出
>>> print(json.dumps({'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}, sort_keys=True, indent=4)) 
{
 "a": "str", 
 "b": 11.1, 
 "c": true, 
 "d": null, 
 "e": 10, 
 "f": [
  1, 
  2, 
  3
 ], 
 "g": [
  4, 
  5, 
  6
 ]
}
로그인 후 복사

separators参数: 尽管indent参数可以使得数据存储的格式变得更加优雅、可读性更强,但是那是通过添加一些冗余的空白字符进行填充的。当json被用于网络数据通信时,应该尽可能的减少无用的数据传输,这样可以节省贷款并加快数据传输速度。json模块序列化Python对象后得到的json字符串中的','号和':'号分隔符后默认都会附加一个空白字符,我们可以通过separators参数重新指定分隔符,从而去除无用的空白字符;

  1. 该参数的值应该是一个tuple(item_separator, key_separator)

  2. 如果indent是None,其默认值为(', ', ': ')

  3. 如果indent不为None,则默认值为(',', ': ')

  4. 我们可以通过为separator赋值为(',', ':')来消除空白字符


>>> json.dumps({'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)})
'{"a": "str", "c": true, "b": 11.1, "e": 10, "d": null, "g": [4, 5, 6], "f": [1, 2, 3]}'

>>> json.dumps({'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}, separators=(',',':'))
'{"a":"str","c":true,"b":11.1,"e":10,"d":null,"g":[4,5,6],"f":[1,2,3]}'
로그인 후 복사

反序列化


# 反序列化
>>> json.loads('{"a": "str", "c": true, "b": 11.1, "e": 10, "d": null, "g": [4, 5, 6], "f": [1, 2, 3]}')
{'c': True, 'e': 10, 'a': 'str', 'g': [4, 5, 6], 'd': None, 'f': [1, 2, 3], 'b': 11.1}

>>> json.loads('{"a":"str","c":true,"b":11.1,"e":10,"d":null,"g":[4,5,6],"f":[1,2,3]}')
{'c': True, 'e': 10, 'a': 'str', 'g': [4, 5, 6], 'd': None, 'f': [1, 2, 3], 'b': 11.1}
로그인 후 복사

dump()与load()函数示例


# 序列化到文件中
>>> with open('test.json', 'w') as fp:
...  json.dump({'a':'str中国', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}, fp, indent=4)

# 反序列化文件中的内容
>>> with open('test.json', 'r') as fp:
...  json.load(fp)
{'e': 10, 'g': [4, 5, 6], 'b': 11.1, 'c': True, 'd': None, 'a': 'str中国', 'f': [1, 2, 3]}
로그인 후 복사

需要说明的是: 如果试图使用相同的fp重复调用dump()函数去序列化多个对象(或序列化同一个对象多次),将会产生一个无效的JSON文件,也就是说对于一个fp只能调用一次dump()。

4. 实例:自定义数据类型的序列化/反序列化

Python是面向对象的编程语言,我们可以自定义需要的数据类型;实际工作中,我们常常会用到自定义数据类型的序列化与反序列化操作。要实现自定义数据类型的序列化与反序列化有两种方式:

  1. 通过转换函数实现

  2. 通过继承JSONEncoder和JSONDecoder类实现

首先来自定义一个数据类型


class Student(object):
 def init(self, name, age, sno):
  self.name = name
  self.age = age
  self.sno = sno
 
 def repr(self):
  return 'Student [name: %s, age: %d, sno: %d]' % (self.name, self.age, self.sno)
로그인 후 복사
로그인 후 복사

直接调用dumps()方法会引发TypeError错误:


>>> stu = Student('Tom', 19, 1)
>>> print(stu)
Student [name: Tom, age: 19, sno: 1]
>>>
>>> json.dumps(stu)
...
TypeError: Student [name: Tom, age: 19, sno: 1] is not JSON serializable
로그인 후 복사

上面的异常信息中指出:stu对象不可以被序列化为JSON个数的数据。那么我们分别通过“编写转换函数” 和 “继承JSONEncoder和JSONDecoder类” 来实现对这个自定义数据类型的JSON序列化和反序列化。

方法1:编写转换函数

那么这个转换函数要完成哪两个数据类型之间的转换呢? 从上面列出的JSON与Python数据类型的对应表中可知,JSON中的object对应的是Python中的dict,因此要对Python中的自定义数据类型的对象进行序列化,就需要先把这个对象转换成json模块可以直接进行序列化dict类型。由此可知,这个转换函数是要完成的是Python对象(不是JSON对象)与dict之间的相互转换,且序列化时转换过程是“Python对象 --> dict --> JSON object”,反序列化的过程是“JSON object -> dict --> Python对象”。所以,我们需要编写两个转换函数来分别实现序列化和反序列化时的转换过程。


def obj2dict(obj):
 d = {}
 d['class'] = obj.class.name
 d['module'] = obj.module
 d.update(obj.dict)
 return d

def dict2obj(d):
 if 'class' in d:
  class_name = d.pop('class')
  module_name = d.pop('module')
  module = import(module_name)
  class_ = getattr(module, class_name)
  args = dict((key.encode('ascii'), value) for key, value in d.items())
  instance = class_(**args)
 else:
  instance = d
 return instance
로그인 후 복사

继承JSONEncoder实现反序列化时还有一个额外的作用,就是可以通过iterencode()方法把一个很大的数据对象分多次进行序列化,这对于网络传输、磁盘持久化等情景非常有用。


>>> for chunk in MyJSONEncoder().iterencode(stu):
...  print(chunk)
...
{
"class"
:
"Student"
,
"name"
:
"Tom"
,
"module"
:
"main"
,
"sno"
:
1
,
"age"
:
19
}
로그인 후 복사

大数据对象序列化网络传输伪代码:


for chunk in JSONEncoder().iterencode(bigobject):
 mysocket.write(chunk)
로그인 후 복사

序列化测试:


>>> import json

>>> obj2dict(stu)
{'sno': 1, 'module': 'main', 'age': 19, 'class': 'Student', 'name': 'Tom'}

>>> json.dumps(obj2dict(stu))
'{"sno": 1, "module": "main", "age": 19, "class": "Student", "name": "Tom"}'

>>> json.dumps(stu, default=obj2dict)
'{"sno": 1, "module": "main", "age": 19, "class": "Student", "name": "Tom"}'
로그인 후 복사

json.dumps(stu, default=obj2dict) 等价于 json.dumps(obj2dict(stu))

反序列化测试:


>>> json.loads('{"sno": 1, "module": "main", "age": 19, "class": "Student", "name": "Tom"}')
{u'sno': 1, u'module': u'main', u'age': 19, u'name': u'Tom', u'class': u'Student'}

>>> dict2obj(json.loads('{"sno": 1, "module": "main", "age": 19, "class": "Student", "name": "Tom"}'))
Student [name: Tom, age: 19, sno: 1]

>>> json.loads('{"sno": 1, "module": "main", "age": 19, "class": "Student", "name": "Tom"}', object_hook=dict2obj)
Student [name: Tom, age: 19, sno: 1]
로그인 후 복사

json.loads(JSON_STR, object_hook=dict2obj) 等价于 dict2obj(json.loads(JSON_STR))

方法2:继承JSONEncoder和JSONDecoder实现子类


import json

class MyJSONEncoder(json.JSONEncoder):
 def default(self, obj):
  d = {}
  d['class'] = obj.class.name
  d['module'] = obj.module
  d.update(obj.dict)
  return d

class MyJSONDecoder(json.JSONDecoder):
 def init(self):
  json.JSONDecoder.init(self, object_hook=self.dict2obj)
 
 def dict2obj(self, d):
  if 'class' in d:
   class_name = d.pop('class')
   module_name = d.pop('module')
   module = import(module_name)
   class_ = getattr(module, class_name)
   args = dict((key.encode('ascii'), value) for key, value in d.items())
   instance = class_(**args)
  else:
   instance = d
  return instance
로그인 후 복사

序列化测试:


>>> stu = Student('Tom', 19, 1)

# 方式一:直接调用子类MyJSONEncoder的encode()方法进行序列化
>>> MyJSONEncoder().encode(stu)
'{"class": "Student", "module": "main", "name": "Tom", "age": 19, "sno": 1}'
>>> MyJSONEncoder(separators=(',', ':')).encode(stu)
'{"class":"Student","module":"main","name":"Tom","age":19,"sno":1}'

# 方式二:将子类MyJSONEncoder作为cls参数的值传递给json.dumps()函数
>>> json.dumps(stu, cls=MyJSONEncoder)
'{"class": "Student", "module": "main", "name": "Tom", "age": 19, "sno": 1}'
>>> json.dumps(stu, cls=MyJSONEncoder, separators=(',', ':'))
'{"class":"Student","module":"main","name":"Tom","age":19,"sno":1}'
로그인 후 복사

反序列化测试:


>>> MyJSONDecoder().decode('{"sno": 1, "module": "main", "age": 19, "class": "Student", "name": "Tom"}')
Student [name: Tom, age: 19, sno: 1]
로그인 후 복사

说明: 经过测试发现MyJSONDecoder().decode(JSON_STR) json.loads(JSON_STR, object_hook=dict2obj) 只能在Python 2.7上正确执行,在Python 3.5上无法正确执行;而 json.loads(JSON_STR, cls=MyJSONDecoder) 无论在Python 2.7还是在Python 3.5上都无法正确执行。这说明json模块对于自定义数据类型的反序列化支持还是比较有限的,但是我们也可以通过json.loads(JSON_STR)函数,不指定cls参数来得到一个dict对象,然后自己完成dict到object的转换。

三、pickle模块

pickle模块实现了用于对Python对象结构进行 序列化 和 反序列化 的二进制协议,与json模块不同的是pickle模块序列化和反序列化的过程分别叫做 pickling 和 unpickling:

  1. pickling: 是将Python对象转换为字节流的过程;

  2. unpickling: 是将字节流二进制文件或字节对象转换回Python对象的过程;

1. pickle模块与json模块对比

  1. JSON是一种文本序列化格式(它输出的是unicode文件,大多数时候会被编码为utf-8),而pickle是一个二进制序列化格式;

  2. JOSN是我们可以读懂的数据格式,而pickle是二进制格式,我们无法读懂;

  3. JSON是与特定的编程语言或系统无关的,且它在Python生态系统之外被广泛使用,而pickle使用的数据格式是特定于Python的;

  4. 默认情况下,JSON只能表示Python内建数据类型,对于自定义数据类型需要一些额外的工作来完成;pickle可以直接表示大量的Python数据类型,包括自定数据类型(其中,许多是通过巧妙地使用Python内省功能自动实现的;复杂的情况可以通过实现specific object API来解决)

2. pickle模块使用的数据流格式

上面提到,pickle使用的数据格式是特定于Python的。这使得它不受诸如JSON或XDR的外部标准限值,但是这也意味着非Python程序可能无法重建pickled Python对象。默认情况下,pickle数据格式使用相对紧凑的二进制表示。如果需要最佳大小特征,可以有效的压缩pickled数据。pickletools模块包含可以用于对pickle生成的数据流进行分析的工具。目前有5种不同的协议可以用于pickle。使用的协议越高,就需要更新的Python版本去读取pickle产生的数据:

  1. 协议v0是原始的“人类可读”协议,并且向后倩蓉早期的Python版本;

  2. 协议v1是一个旧的二进制格式,也与早期版本的Python兼容;

  3. 协议v2在Python 2.3中引入,它提供更高效的pickling;

  4. 协议v3是在Python 3.0添加的协议,它明确支持bytes对象,且不能被Python 2.x 进行unpickle操作;这是默认协议,也是当需要兼容其他Python 3版本时被推荐使用的协议;

  5. 协议4是在Python 3.4添加的协议,它添加了对极大对象的支持,pickling更多种类的对象,以及一些数据格式的优化。

说明: Python 2.x中默认使用的是协议v0,如果协议指定为赋值或HIGHEST_PROTOCOL,将使用当前可用的最高协议版本;Python 3.x中默认使用的是协议v3,它兼容其他Python 3版本,但是不兼容Python 2。

注意: 序列化(Serialization)是一个比持久化(Persistence)更加原始的概念;虽然pickle可以读写文件对象,但是它不处理持久化对象的命名问题,也不处理对持久化对象的并发访问问题(甚至更复杂的问题)。pickle模块可以将复杂对象转换为字节流,并且可以将字节流转换为具有相同内部结构的对象。或许最可能对这些字节流做的事情是将它们写入文件,但是也可以对它们进行网络传输或将它们存储在数据库中。shelve模块提供了一个简单的接口用于在DBM风格的数据库文件上对对象进行pickle和unpickle操作。

3. pickle模块提供的相关函数

pickle模块提供的几个序列化/反序列化的函数与json模块基本一致:


# 将指定的Python对象通过pickle序列化作为bytes对象返回,而不是将其写入文件
dumps(obj, protocol=None, *, fix_imports=True)

# 将通过pickle序列化后得到的字节对象进行反序列化,转换为Python对象并返回
loads(bytes_object, *, fix_imports=True, encoding="ASCII", errors="strict")

# 将指定的Python对象通过pickle序列化后写入打开的文件对象中,等价于`Pickler(file, protocol).dump(obj)`
dump(obj, file, protocol=None, *, fix_imports=True)

# 从打开的文件对象中读取pickled对象表现形式并返回通过pickle反序列化后得到的Python对象
load(file, *, fix_imports=True, encoding="ASCII", errors="strict")
로그인 후 복사

说明: 上面这几个方法参数中,*号后面的参数都是Python 3.x新增的,目的是为了兼容Python 2.x,具体用法请参看官方文档。

4. 实例:内置数据类型的序列化/反序列化

Python 2.x


>>> import pickle
>>> 
>>> var_a = {'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}

# 序列化
>>> var_b = pickle.dumps(var_a)
>>> var_b
"(dp0\nS'a'\np1\nS'str'\np2\nsS'c'\np3\nI01\nsS'b'\np4\nF11.1\nsS'e'\np5\nI10\nsS'd'\np6\nNsS'g'\np7\n(I4\nI5\nI6\ntp8\nsS'f'\np9\n(lp10\nI1\naI2\naI3\nas."

# 反序列化
>>> var_c = pickle.loads(var_b)
>>> var_c
{'a': 'str', 'c': True, 'b': 11.1, 'e': 10, 'd': None, 'g': (4, 5, 6), 'f': [1, 2, 3]}
로그인 후 복사

Python 3.x


>>> import pickle
>>>
>>> var_a = {'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}

# 序列化
>>> var_b = pickle.dumps(var_a)
>>> var_b
b'\x80\x03}q\x00(X\x01\x00\x00\x00eq\x01K\nX\x01\x00\x00\x00aq\x02X\x03\x00\x00\x00strq\x03X\x01\x00\x00\x00fq\x04]q\x05(K\x01K\x02K\x03eX\x01\x00\x00\x00gq\x06K\x04K\x05K\x06\x87q\x07X\x01\x00\x00\x00bq\x08G@&333333X\x01\x00\x00\x00cq\t\x88X\x01\x00\x00\x00dq\nNu.'

# 反序列化
>>> var_c = pickle.loads(var_b)
>>> var_c
{'e': 10, 'a': 'str', 'f': [1, 2, 3], 'g': (4, 5, 6), 'b': 11.1, 'c': True, 'd': None}
로그인 후 복사

dump()与load()


>>> import pickle
>>>
>>> var_a = {'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}

# 持久化到文件
>>> with open('pickle.txt', 'wb') as f:
...  pickle.dump(var_a, f)
...

# 从文件中读取数据
>>> with open('pickle.txt', 'rb') as f:
...  var_b = pickle.load(f)
...
>>> var_b
{'e': 10, 'a': 'str', 'f': [1, 2, 3], 'g': (4, 5, 6), 'b': 11.1, 'c': True, 'd': None}
>>>
로그인 후 복사

说明:

  1. 默认情况下Python 2.x中pickled后的数据是字符串形式,需要将它转换为字节对象才能被Python 3.x中的pickle.loads()反序列化;Python 3.x中pickling所使用的协议是v3,因此需要在调用pickle.dumps()时指定可选参数protocol为Python 2.x所支持的协议版本(0,1,2),否则pickled后的数据不能被被Python 2.x中的pickle.loads()反序列化;

  2. Python 3.x中pickle.dump()和pickle.load()方法中指定的文件对象,必须以二进制模式打开,而Python 2.x中可以以二进制模式打开,也可以以文本模式打开。

5. 实例:自定义数据类型的序列化/反序列化

首先来自定义一个数据类型:


class Student(object):
 def init(self, name, age, sno):
  self.name = name
  self.age = age
  self.sno = sno
 
 def repr(self):
  return 'Student [name: %s, age: %d, sno: %d]' % (self.name, self.age, self.sno)
로그인 후 복사
로그인 후 복사

pickle模块可以直接对自定数据类型进行序列化/反序列化操作,无需编写额外的处理函数或类。


>>> stu = Student('Tom', 19, 1)
>>> print(stu)
Student [name: Tom, age: 19, sno: 1]

# 序列化
>>> var_b = pickle.dumps(stu)
>>> var_b
b'\x80\x03cmain\nStudent\nq\x00)\x81q\x01}q\x02(X\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00Tomq\x04X\x03\x00\x00\x00ageq\x05K\x13X\x03\x00\x00\x00snoq\x06K\x01ub.'

# 反序列化
>>> var_c = pickle.loads(var_b)
>>> var_c
Student [name: Tom, age: 19, sno: 1]

# 持久化到文件
>>> with open('pickle.txt', 'wb') as f:
...  pickle.dump(stu, f)
...

# 从文件总读取数据
>>> with open('pickle.txt', 'rb') as f:
...  pickle.load(f)
...
Student [name: Tom, age: 19, sno: 1]
로그인 후 복사

四、shelve模块

shelve是一个简单的数据存储方案,类似key-value数据库,可以很方便的保存python对象,其内部是通过pickle协议来实现数据序列化。shelve只有一个open()函数,这个函数用于打开指定的文件(一个持久的字典),然后返回一个shelf对象。shelf是一种持久的、类似字典的对象。它与“dbm”的不同之处在于,其values值可以是任意基本Python对象--pickle模块可以处理的任何数据。这包括大多数类实例、递归数据类型和包含很多共享子对象的对象。keys还是普通的字符串。


open(filename, flag='c', protocol=None, writeback=False)
로그인 후 복사

flag 参数表示打开数据存储文件的格式,可取值与dbm.open()函数一致:

描述
'r'以只读模式打开一个已经存在的数据存储文件
'w'以读写模式打开一个已经存在的数据存储文件
'c'以读写模式打开一个数据存储文件,如果不存在则创建
'n'总是创建一个新的、空数据存储文件,并以读写模式打开

protocol 参数表示序列化数据所使用的协议版本,默认是pickle v3;

writeback 参数表示是否开启回写功能。

我们可以把shelf对象当dict来使用--存储、更改、查询某个key对应的数据,当操作完成之后,调用shelf对象的close()函数即可。当然,也可以使用上下文管理器(with语句),避免每次都要手动调用close()方法。

实例:内置数据类型操作


# 保存数据
with shelve.open('student') as db:
 db['name'] = 'Tom'
 db['age'] = 19
 db['hobby'] = ['篮球', '看电影', '弹吉他']
 db['other_info'] = {'sno': 1, 'addr': 'xxxx'}

# 读取数据
with shelve.open('student') as db:
 for key,value in db.items():
  print(key, ': ', value)
로그인 후 복사

输出结果:

name : Tom
age : 19
hobby : ['篮球', '看电影', '弹吉他']
other_info : {'sno': 1, 'addr': 'xxxx'}

实例:自定义数据类型操作


# 自定义class
class Student(object):
 def init(self, name, age, sno):
  self.name = name
  self.age = age
  self.sno = sno
 
 def repr(self):
  return 'Student [name: %s, age: %d, sno: %d]' % (self.name, self.age, self.sno)

# 保存数据
tom = Student('Tom', 19, 1)
jerry = Student('Jerry', 17, 2)

with shelve.open("stu.db") as db:
 db['Tom'] = tom
 db['Jerry'] = jerry

# 读取数据
with shelve.open("stu.db") as db:
 print(db['Tom'])
 print(db['Jerry'])
로그인 후 복사

输出结果:
Student [name: Tom, age: 19, sno: 1]
Student [name: Jerry, age: 17, sno: 2]

五、总结

1. 对比

json模块常用于编写web接口,将Python数据转换为通用的json格式传递给其它系统或客户端;也可以用于将Python数据保存到本地文件中,缺点是明文保存,保密性差。另外,如果需要保存费内置数据类型需要编写额外的转换函数或自定义类。

pickle模块和shelve模块由于使用其特有的序列化协议,其序列化之后的数据只能被Python识别,因此只能用于Python系统内部。另外,Python 2.x 和 Python
 3.x 默认使用的序列化协议也不同,如果需要互相兼容需要在序列化时通过protocol参数指定协议版本。除了上面这些缺点外,pickle模块和shelve模块相对于json模块的优点在于对于自定义数据类型可以直接序列化和反序列化,不需要编写额外的转换函数或类。

shelve模块可以看做是pickle模块的升级版,因为shelve使用的就是pickle的序列化协议,但是shelve比pickle提供的操作方式更加简单、方便。shelve模块相对于其它两个模块在将Python数据持久化到本地磁盘时有一个很明显的优点就是,它允许我们可以像操作dict一样操作被序列化的数据,而不必一次性的保存或读取所有数据。

2. 建议

  1. 需要与外部系统交互时用json模块;

  2. 需要将少量、简单Python数据持久化到本地磁盘文件时可以考虑用pickle模块;

  3. 需要将大量Python数据持久化到本地磁盘文件或需要一些简单的类似数据库的增删改查功能时,可以考虑用shelve模块。

3. 附录

要实现的功能 可以使用的api
将Python数据类型转换为(json)字符串 json.dumps()
将json字符串转换为Python数据类型 json.loads()
将Python数据类型以json形式保存到本地磁盘 json.dump()
将本地磁盘文件中的json数据转换为Python数据类型 json.load()
将Python数据类型转换为Python特定的二进制格式 pickle.dumps()
将Python特定的的二进制格式数据转换为Python数据类型 pickle.loads()
将Python数据类型以Python特定的二进制格式保存到本地磁盘 pickle.dump()
将本地磁盘文件中的Python特定的二进制格式数据转换为Python数据类型 pickle.load()
以类型dict的形式将Python数据类型保存到本地磁盘或读取本地磁盘数据并转换为数据类型 shelve.open()

위 내용은 Python 데이터 직렬화(json, pickle, shelve)에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿