'''
持久存储数据以便长期使用包括两个方面:在对象的内存中表示和存储格式之间来回转换数据,以及处理转换后数据的存储区。
标准库包含很多模块可以处理不同情况下的这两个方面

有两个模块可以将对象转换为一种可传输或存储的格式(这个过程被称为序列化)。最常用的是使用pickle持久存储,因为它可以与其他一些具体存储序列化数据的模块集成,如shelve。
而对基于web的应用,json更为常用,因为它能更好地与现有的web服务存储工具集成

一旦将内存中对象转化为一种可保存的格式,那么下一步就是确定如何存储这个数据。如果数据不需要以某种方式索引,则按照顺序先后写入序列化对象即可。
Python包括一组模块可以在一个简单的数据库中存储键值对,需要索引查找时会使用某种DBM变形格式

要利用DBM的格式,最直接的方式是使用shelve。可以打开shelve文件,通过一个类似字典的API来访问。
保存到数据库的对象会自动"腌制"并保存,而无须调用者做任何额外的工作

不过shelve有一个缺点,使用默认接口时,没有办法预测将使用哪一个DBM格式,因为shelve会根据创建数据库的系统上有哪些可用的库来选择一个格式。
如果应用不需要在配置有不同的库的主机之间共享数据库文件,那么选择哪一种并不重要。不过,如果必须保证可移植性,则可以使用这个模块中的某个类来确保选择一个特定的格式

对于web应用,由于这些应用已经在处理json格式的数据,因此可以使用json和dbm提供另一种持久存储机制。
直接使用dbm会比使用shelve多做一些工作,因为DBM数据库键和值都必须是字符串,,而且在数据库中访问值时不会自动创建对象。

还有xml,csv等格式
'''

  

(一)pickle:对象序列化

import pickle
'''
pickle模块实现了一个算法,可以将一个Python对象转换为一系列字节。这个过程被称为序列化。
可以传输或存储表示对象的字节流,然后再重新构造来创建有相同性质的新对象。
'''

# 注意:
'''
pickle的文档明确指出它不提供任何安全保证。实际上,对数据解除"腌制"(反序列化)可以执行任意的代码。
使用pickle模块完成进程间通信或数据存储时要当心,另外不要相信未经过安全验证的数据。
'''

  

1.编码和解码字符串中的数据

import pickle
'''
可以使用dumps将Python中对象进行序列化,也可以使用loads将序列化的对象转换成Python中的对象
'''
d = {"a": 1, "b": 2}

data_string = pickle.dumps(d)
print(data_string)

# 传入序列化对象
data = pickle.loads(data_string)  # b'\x80\x03}q\x00(X\x01\x00\x00\x00aq\x01K\x01X\x01\x00\x00\x00bq\x02K\x02u.'
print(data["a"] + data["b"])  # 3
'''
dumps(python对象) --> 序列化对象
loads(序列化对象) --> Python对象

默认地,pickle将以一种二进制格式写入,在Python3程序之间共享时这种兼容性最好

数据序列化后,可以写到一个文件、套接字、管道或者其它位置,之后可以读取这个文件,将文件进行反序列化,以便用同样的值构造一个新对象
'''

# 注意:可以序列化Python中的大部分常见对象
class A:
    a = "aaa"

a = A()
obj = pickle.dumps(a)
# 反序列化之后的对象和原来的对象是一样的,但是不是同一个对象
print(pickle.loads(obj) is a)  # False
print(pickle.loads(obj).a)  # aaa

# 除此之外,pickle还可以将序列化dump到一个文件里,然后从文件里面load
'''
函数分别是dump和load
pickle.dump(python对象, f)
pickle.load(f)

和不涉及文件的dumps、loads类似
pickle.dumps(Python对象)  -->会有返回值,obj
pickle.loads(obj)

操作类似,不再演示
'''

  

2.处理流

import pickle
import io
'''
除了dumps、loads,pickle还提供了一些便利的函数来处理类似文件的流。
可以向一个流写多个对象,然后从流读取这些对象,而无须事先知道要写多个对象或者这些对象有多大。
'''
d = {"a": 1, "b": 2}
l = [1, 2, 3]
s = {1, 1, 3}
data = [d, l, s]

out_s = io.BytesIO()
for o in data:
    pickle.dump(o, out_s)
    out_s.flush()

in_s = io.BytesIO(out_s.getvalue())

while True:
    try:
        o = pickle.load(in_s)
        print(o)
    except EOFError:
        break
'''
{'a': 1, 'b': 2}
[1, 2, 3]
{1, 3}
'''

  

3.重构对象的问题

import pickle
import sys
'''
处理定制类时,腌制的类必须出现在读取pickle的进程所在的命名空间里。
只会腌制这个实例的数据,而不是类定义。类名用于查找构造函数,以便在解除腌制时创建新对象。

比如我在A.py中定义了一个类Foo,然后将其实例对象序列化。
我在B.py中将其反序列化,是会报错的,因为根本就有没有Foo这个类,如果from A import Foo之后,那么便不会报错。
说明腌制的类必须出现在读取pickle的进程所在的命名空间里
'''

  

4.不可腌制的对象

import pickle
'''
并不是所有对象都是可腌制的。套接字、文件句柄、数据库连接以及其他运行时状态依赖于操作系统或其他进程的对象,其可能无法用一种有意义的方式保存。
如果对象包含不可腌制的属性,则可以定义__getstate__和__setstate__来返回所腌制实例的状态的一个子集

__getstate__方法必须返回一个对象,其中包含所腌制对象的内部状态。表示状态的一种便利方式是使用字典,不过值可以是任意的可腌制对象。
保存状态,然后在从pickle加载对象时将所保存的状态传入__setstate__
'''

class A:

    def __init__(self):
        self.name = "mashiro"
        self.age = 16

    def __getstate__(self):
        print("__getstate__")
        return {"name": self.name, "age": self.age}

    def __setstate__(self, state):
        print("__setstate__")
        print(state)

a = A()
# 当dumps的时候,会触发__getstate__方法,要有一个返回值
dump_obj = pickle.dumps(a)  # __getstate__
# 当loads的时候,会触发__setstate__方法,__getstate__方法的返回值会传给state
load_obj = pickle.loads(dump_obj)
'''
__setstate__
{'name': 'mashiro', 'age': 16}
'''

# 而且pickle协议会自动处理对象之间的循环引用,所以复杂数据结构不需要任何特殊的处理。

  

2.dbm:Unix-键值数据库

'''
在一些小型程序中,不需要关系型数据库时,可以方便的用持久字典来存储键值对,和python中的字典非常类似。而且dbm的键和值都必须是str或者bytes类型
'''
import dbm

'''
这里第一个参数直接传入文件名,第二个参数表示模式
常见的模式:
r:可读,默认就是这个模式
w:可读可写
但是r、w,都必须确保文件已经存在,否则报错。

c:可读可写,文件不存在时会创建
n:可读可写,但总是会创建一个新的文件,也就是说如果创建同名文件,那么之前的内容都会被清空,也就是起不到追加的效果。

因此我们平常的模式一般都会选择c

第三个参数是权限,这个在windows下基本不用,是一组用八进制表示的数字,默认是0o666,都是可读可写不可执行
'''
db = dbm.open("store", "c")

# 打开文件之后,就可以存储值了
# 注意key和value都必须是str或者bytes类型
db["name"] = "satori"
db["age"] = "16"
db["gender"] = "f"
db["anime"] = "东方地灵殿"

# 关闭文件,将内容写到磁盘上
db.close()

################################################################
# 打开文件
db = dbm.open("store", "c")
print(db.keys())  # [b'name', b'age', b'gender', b'anime']
for key in db.keys():
    print(f"key={key}, value={db[key]}")
    '''
    key=b'name', value=b'satori'
    key=b'age', value=b'16'
    key=b'gender', value=b'f'
    key=b'anime', value=b'\xe4\xb8\x9c\xe6\x96\xb9\xe5\x9c\xb0\xe7\x81\xb5\xe6\xae\xbf'
    '''

  会多出这么三个文件

3.shelve:对象的持久存储

'''

shelve和dbm比较类似,但是功能远比dbm强大,因为它可以持久化任意对象
'''
import shelve

# 参数flag默认是c,因此我们只需要传入文件名就可以了,这个是自动追加在后面的
# 也就是说我写完之后,再次打开继续写的话,只会追加不会清空
sh = shelve.open("shelve")

sh["dict"] = {"name": "satori", "age": 16}
sh["list"] = [1, 2, 3, 4]
sh["set"] = {1, 2, 3, 2}

# 写完之后关闭文件,刷到内存里面
# 关闭之后就无法操作了
sh.close()

# 下面我们就可以操作数据了,下面的代码即便写在另一个py文件里面也是可以的
sh2 = shelve.open("shelve")
print(sh2["dict"], sh2["dict"].keys())  # {'name': 'satori', 'age': 16} dict_keys(['name', 'age'])
print(sh2["list"], sum(sh2["list"]))  # [1, 2, 3, 4] 10
print(sh2["set"])  # {1, 2, 3}
sh2.close()

# 可以看到,拿出来的就是原生的对象,可以直接用来进行操作的。那我们看看自己定义的类可不可以呢?
sh3 = shelve.open("shelve")

class A:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @property
    def print_info(self):
        return f"my name is {self.name}, age is {self.age}"

a = A("satori", 16)
# 将这个类和类的一个实例对象存储进去
sh3["A"] = A
sh3["a"] = a
sh3.close()

######################################
sh4 = shelve.open("shelve")

# sh4["A"]拿到A这个类,传入参数,调用方法
print(sh4["A"]("mashiro", "17").print_info)  # my name is mashiro, age is 17

# sh4["a"]拿到a这个实例对象,直接调用方法
print(sh4["a"].print_info)  # my name is satori, age is 16

# 我们发现依旧是可以的,说明了shelve这个模块真的很强大

  

# 我们再来看一个例子
import shelve

sh = shelve.open("shelve")
sh["list"] = [1, 2, 3]
sh["str"] = "mashiro"
sh.close()

##############################
sh = shelve.open("shelve")
sh["list"].append("xxxx")
sh["str"] = "satori"
sh.close()

#######################
sh = shelve.open("shelve")
print(sh["list"])  # [1, 2, 3]
print(sh["str"])  # satori
'''
分析结果,第一次打开文件我们创建两个键值对
sh["list"] = [1, 2, 3]
sh["str"] = "mashiro"

第二次打开文件,修改了两个键的值
第三次打开文件,打印。但是我们发现sh["str"]改变了,但是sh["list"]没有改变,这是为什么?
首先sh["str"] = "satori"很好理解,但是为什么sh["list"]没有变?
因为=,我们是直接赋值,将这一块内存里面的值给换掉,而sh["list"]我们是做append操作,这只是在原来的基础上进行修改
shelve默认情况下是不会记录,持久化对象的修改的,除非你是创建新的对象,或者是把原来的对象给换掉
如果是在原来的基础上(可变类型),比如列表、字典,进行添加或者删除操作,这些是不会被记录的
所以:sh["list"]=[1, 2, 3]  sh["list"].append("xxxx")  --->sh["list"]仍是[1, 2, 3]不会是[1, 2, 3, "xxx"]
因为shelve没有记录对象自身的修改,如果我想得到期望的结果,一种方法是把对象整体换掉
sh["list"] = [1, 2, 3, "xxxx"],这样等于是重新赋值,是可行的。但是有时候我们不知道列表里面内容,或者列表里面的内容是一些函数、类什么的、不好写的话,该咋办呢?
其实我们在打开文件的时候,还可以加上一个参数,叫做writeback
'''

  

import shelve

sh = shelve.open("shelve")
sh["list"] = [1, 2, 3]
sh["str"] = "mashiro"
sh.close()

##############################
# 如果我们需要进行修改,那么加上一个writeback=True就可以了,从名字也能看出来
# 这是会将修改的内容从新写回去
sh = shelve.open("shelve", writeback=True)
sh["list"].append("xxxx")
sh["str"] = "satori"
sh.close()

#######################
sh = shelve.open("shelve")
print(sh["list"])  # [1, 2, 3, 'xxxx']
print(sh["str"])  # satori
'''
可以看到都发生改变了,但是这个参数有缺陷,就是会有额外的内存消耗。当我们加上writeback=True的时候shelve会将我们读取的对象都放到一个内存缓存当中。
比如说我们获取了20持久化的对象,但是我们只修改了一个,剩余的19个只是查看并没有做修改,但当我们sh.close()的时候,会将这20个对象都写回去
因为shelve不知道你会对哪个对象进行修改,于是不管你是查看还是修改,都会放到缓存当中,然后再一次性都写回去。
这样会造成两点:
1.对象放到内存缓存当中,等于是重新拷贝了一份,因为我们读取文件已经到内存当中了,而shelve又把我们使用的对象放到内存的另一片空间中
2.写入数据,我们明明只修改了一份数据,但是它把20份都重新写回去了,这样会造成性能上的问题,导致效率会降低。
因此加不加这个参数,由具体情况决定
'''

  

4.sqlite3:嵌入式关系数据库

'''
不做介绍,因为这个数据库真的没有啥用,不想浪费时间。
因为Python3标准库这本书太特么厚了
'''

  

5.xml.etree.ElementTree:xml操纵API

'''
不做介绍,因为这个xml这种格式基本上没人用了。都用json,除非一些非常老的金融公司还会用xml,否则基本都不用xml,遇见了再在网上搜吧,不想浪费时间。
因为Python3标准库这本书太特么厚了
'''

  

6.csv:逗号分隔符文件

'''
不做介绍,因为csv文件的话,我一般不用csv模块取处理,而是使用一个更强大的第三方库pandas,这个库能做到的远远比csv模块多得多,所以建议了解pandas,csv模块这里就不做介绍,不想浪费时间
因为Python3标准库这本书太特么厚了
'''

  

8.Python3标准库--数据持久存储与交换的更多相关文章

  1. 11.python3标准库--使用进程、线程和协程提供并发性

    ''' python提供了一些复杂的工具用于管理使用进程和线程的并发操作. 通过应用这些计数,使用这些模块并发地运行作业的各个部分,即便是一些相当简单的程序也可以更快的运行 subprocess提供了 ...

  2. 7.Python3标准库--文件系统

    ''' Python的标准库中包含大量工具,可以处理文件系统中的文件,构造和解析文件名,还可以检查文件内容. 处理文件的第一步是要确定处理的文件的名字.Python将文件名表示为简单的字符串,另外还提 ...

  3. Python3 标准库

    Python3标准库 更详尽:http://blog.csdn.net/jurbo/article/details/52334345 文本 string:通用字符串操作 re:正则表达式操作 diff ...

  4. python023 Python3 标准库概览

    Python3 标准库概览 操作系统接口 os模块提供了不少与操作系统相关联的函数. >>> import os >>> os.getcwd() # 返回当前的工作 ...

  5. python3标准库总结

    Python3标准库 操作系统接口 os模块提供了不少与操作系统相关联的函数. ? 1 2 3 4 5 6 >>> import os >>> os.getcwd( ...

  6. 1.Python3标准库--前戏

    Python有一个很大的优势便是在于其拥有丰富的第三方库,可以解决很多很多问题.其实Python的标准库也是非常丰富的,今后我将介绍一下Python的标准库. 这个教程使用的书籍就叫做<Pyth ...

  7. 比较两个文件的异同Python3 标准库difflib 实现

    比较两个文件的异同Python3 标准库difflib 实现 对于要比较两个文件特别是配置文件的差异,这种需求很常见,如果用眼睛看,真是眼睛疼. 可以使用linux命令行工具diff a_file b ...

  8. 9.Python3标准库--数据压缩与归档

    ''' 尽管现代计算机系统的存储能力日益增长,但生成数据的增长是永无休止的. 无损(lossless)压缩算法以压缩或解压缩数据花费的时间来换取存储数据所需要的空间,以弥补存储能力的不足. Pytho ...

  9. 3.Python3标准库--数据结构

    (一)enum:枚举类型 import enum ''' enum模块定义了一个提供迭代和比较功能的枚举类型.可以用这个为值创建明确定义的符号,而不是使用字面量整数或字符串 ''' 1.创建枚举 im ...

随机推荐

  1. Keywords Search HDU - 2222(ac自动机板题。。)

    求一个字符串上有多少个匹配的单词 看着卿学姐的板子写的 指针形式: #include <iostream> #include <cstdio> #include <sst ...

  2. Closest Number in Sorted Array

    Given a target number and an integer array A sorted in ascending order, find the index i in A such t ...

  3. 最长上升子序列nlogn算法

    LIS问题是经典的动态规划问题,它的状态转移相信大家都很熟悉: f[i] = f[k] + 1  (k < i 且 A[k] < A[i]) 显然这样做复杂度是O(n^2) 有没有更快的算 ...

  4. UTF-8和GBK编码之间的区别(页面编码、数据库编码区别)以及在实际项目中的应用

    第一节:UTF-8和GBK编码概述 UTF-8 (8-bit Unicode Transformation Format) 是一种针对Unicode的可变长度字符编码,又称万国码,它包含全世界所有国家 ...

  5. [JOI 2015 Final] 分蛋糕 2

    link 试题分析 容易发现性质,选择的是一段区间,但是贪心无法去维护这件事情,所以考虑$dp$,且我们只要去设计关于$JOI$的选择. 设$dp(i,j)$为现在要在$[l,r]$区间内选择,然后就 ...

  6. python函数:字符串函数示例

    优先掌握的操作 #作用:名字,性别,国籍,地址等描述信息 #定义:在单引号\双引号\三引号内,由一串字符组成 name='egon' #优先掌握的操作: #1.按索引取值(正向取+反向取) :只能取 ...

  7. cin/cout与scanf/printf的比较

    转自http://www.cnblogs.com/penelope/articles/2426577.html  cin .cout   基本说明: cin是标准输入流对象,代表标准输入设备(键盘), ...

  8. codeforces 55D 数位dp

    D. Beautiful numbers time limit per test 4 seconds memory limit per test 256 megabytes input standar ...

  9. MySQL语句查看各个数据库占用空间

    select table_schema, sum(DATA_LENGTH)+sum(INDEX_LENGTH) from information_schema.tables group by tabl ...

  10. CSS 定位相关属性 :position

    我们平时经常用margin来进行布局,但是遇到一些盒子不规律布局时,用margin就有点麻烦了,这个时候我们可以用position. position:参数 参数分析: 一.absolute: 相对父 ...