在数据存储的上下文中,序列化是将数据结构或对象状态转换为可以存储(例如,在文件或存储缓冲器中)或稍后传输和重构的格式的过程。
在序列化中,对象被转换为可以存储的格式,以便以后能够对其进行反序列化并从序列化格式重新创建原始对象。
Pickle
Pickling
是将Python对象层次结构转换为要写入文件的字节流(通常不是人类可读的)的过程,这也称为序列化。Unpickling
是反向操作,将字节流转换回工作的Python对象层次结构。
Pickle是操作上最简单的存储对象的方法。 Python Pickle模块是一种面向对象的方式,可以直接以特殊的存储格式存储对象。
它能做什么?
- Pickle可以非常轻松地存储和复制字典和列表。
- 存储对象属性并将它们还原到同一个状态。
Pickle不能做什么?
- 它不保存对象代码,只有它的属性值。
- 它无法存储文件句柄或连接套接字。
简而言之,pickling
是一种在文件中存储和检索数据变量的方法,其中变量可以是列表,类等。
要使用Pickle
,必须要 -
- 导入
Pickle
- 将变量写入文件
例如
pickle.dump(mystring, outfile, protocol),
其中第三个参数:protocol
是可选的,
要使用unpickling
,必须要 -
- 导入
Pickle
- 将变量写入文件
例如
myString = pickle.load(inputfile)
pickle
接口提供四种不同的方法。
dump()
- 序列化到打开的文件(类文件对象)。dumps()
- 序列化为字符串load()
- 从类似开放的对象反序列化。loads()
- 从字符串反序列化。
基于以上程序,下面是“pickle”的一个例子。
import pickle
class Animal:
def __init__(self, number_of_legs, color):
self.number_of_legs = number_of_legs
self.color = color
class Cat(Animal):
def __init__(self, color):
Animal.__init__(self, 4, color)
pussy = Cat('White')
print(str.format('My Cat pussy is {0} and has {1} legs', pussy.color, pussy.number_of_legs))
pickled_pussy = pickle.dumps(pussy)
print("Would you like to see her pickled? Here she is - ")
print(pickled_pussy)
执行上面示例代码,得到以下结果 -
My Cat pussy is White and has 4 legs
Would you like to see her pickled? Here she is -
b'\x80\x03c__main__\nCat\nq\x00)\x81q\x01}q\x02(X\x0e\x00\x00\x00number_of_legsq\x03K\x04X\x05\x00\x00\x00colorq\x04X\x05\x00\x00\x00Whiteq\x05ub.'
因此,在上面的示例中,创建了一个Cat
类的实例,然后将它pickled,将“Cat”实例转换为一个简单的字节数组。
这样,可以轻松地将字节数组存储在二进制文件或数据库字段中,并在以后从存储支持将其恢复为原始格式。
此外,如果要创建带有pickle
对象的文件,可以使用dump()
方法(而不是dumps *()* one
)同时传递打开的二进制文件,并且pickling
结果将自动存储在文件中。
[….]
binary_file = open(my_pickled_Pussy.bin', mode='wb')
my_pickled_Pussy = pickle.dump(Pussy, binary_file)
binary_file.close()
Unpickling
采用二进制数组并将其转换为对象层次结构的过程称为unpickling
。
通过使用pickle模块的load()
函数完成unpickling
过程,并从简单的字节数组返回一个完整的对象层次结构。
在前面的例子中使用load
函数。
import pickle
class Animal:
def __init__(self, number_of_legs, color):
self.number_of_legs = number_of_legs
self.color = color
class Cat(Animal):
def __init__(self, color):
Animal.__init__(self, 4, color)
pussy = Cat('White')
print(str.format('My Cat pussy is {0} and has {1} legs', pussy.color, pussy.number_of_legs))
pickled_pussy = pickle.dumps(pussy)
# print("Would you like to see her pickled? Here she is - ")
# print(pickled_pussy)
pcat = pickle.loads(pickled_pussy)
pcat.color = "black"
print(str.format("PCat is {0}", pcat.color))
print(str.format("Pussy is {0}", pussy.color))
执行上面示例代码,得到以下结果 -
E:\worksp\pycharm\venv\Scripts\python.exe E:/worksp/pycharm/main.py
My Cat pussy is White and has 4 legs
PCat is black
Pussy is White
JSON
JSON(JavaScript Object Notation)已经成为Python标准库的一部分,是一种轻量级的数据交换格式。 人类很容易读写。 它很容易解析和生成。
由于其简单性,JSON是我们存储和交换数据的一种方式,它通过其JSON语法实现,并在许多Web应用程序中使用。 因为它是人类可读的格式,这可能是在数据传输中使用它的原因之一,除了它在使用API时的有效性。
JSON格式数据的示例如下 -
{"EmployID": 40203, "Name": "Maxsu", "Age":54, "isEmployed": True}
Python使用的模块是JSON模块使得使用Json文件变得简单。应该在Python安装中包含(内置)此模块。
下面来看看如何将Python字典转换为JSON并将其写入文本文件。
JSON到Python
读JSON意味着将JSON转换为Python值(对象)。 json
库将JSON解析为Python中的字典或列表。要做到这一点,可使用loads()
函数(从字符串加载),如下所示 -
import json
json_data = '{"EmployeeID": 10010, "EmployeeName": "Maxsu", "Department":"技术部"}'
json2python = json.loads(json_data)
执行上面示例代码,得到以下结果 -
E:\worksp\pycharm\venv\Scripts\python.exe E:/worksp/pycharm/main.py
{'EmployeeID': 10010, 'EmployeeName': 'Maxsu', 'Department': '技术部'}
下面是一个示例json文件:data1.json,
{"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}}
上面的内容(data1.json
)看起来像传统的字典。 可以使用pickle来存储这个文件,但它的输出不是人类可读的形式。
JSON(Java Script Object Notification)是一种非常简单的格式,这也是它受欢迎的原因之一。 现在通过以下程序来打印上面json文件的输出。
import json
with open('data1.json') as f:
config = json.load(f)
print(config)
print('+++++++++++++++++++++++++++++++++++++++++++++++++')
print(type(config))
print('+++++++++++++++++++++++++++++++++++++++++++++++++')
config['newkey'] = 10086
with open("data2.json", 'w') as wf:
json.dump(config, wf)
print(config)
执行上面示例代码,得到以下结果 -
E:\worksp\pycharm\venv\Scripts\python.exe E:/worksp/pycharm/main.py
{'menu': {'id': 'file', 'value': 'File', 'popup': {'menuitem': [{'value': 'New', 'onclick': 'CreateNewDoc()'}, {'value': 'Open', 'onclick': 'OpenDoc()'}, {'value': 'Close', 'onclick': 'CloseDoc()'}]}}}
+++++++++++++++++++++++++++++++++++++++++++++++++
<class 'dict'>
+++++++++++++++++++++++++++++++++++++++++++++++++
{'menu': {'id': 'file', 'value': 'File', 'popup': {'menuitem': [{'value': 'New', 'onclick': 'CreateNewDoc()'}, {'value': 'Open', 'onclick': 'OpenDoc()'}, {'value': 'Close', 'onclick': 'CloseDoc()'}]}}, 'newkey': 10086}
Process finished with exit code 0
上面程序打开json文件(data1.json
)进行读取,获取文件处理程序并传递给json.load
并获取对象。 当打印对象的输出时,可以看到它与json文件相同。 虽然对象的类型是字典,它此时是Python对象。上面加载json文件,添加另一个键值对并将其写回到到另一个json文件中。 现在,如果打开文件:data2.json
,它看起来不同。与之前看到的格式不同。
要想使输出看起来相同(人类可读的格式),将几个参数添加到程序的最后一行,参考如上代码 -
json.dump(conf, fh, indent = 4, separators = (',', ': '))
与pickle
类似,可以使用转储打印字符串并使用load
加载。 以下是一个例子,
YAML
YAML可能是所有编程语言中最人性化的数据序列化标准。
Python yaml模块叫作:pyaml
YAML是JSON的替代品 -
- 人类可读的代码 - YAML是人类可读的格式,因此即使其首页内容也显示在YAML中以表明这一点。
- 紧凑的代码 - 在YAML中,我们使用空格缩进来表示结构而不是括号。
- 关系数据的语法 - 对于内部引用,使用锚点(
&
)和别名(*
)。 - 广泛使用的一个领域是查看/编辑数据结构 - 例如配置文件,调试期间的转储和文档头。
安装YAML
由于yaml不是内置模块,需要手动安装它。 在Windows机器上安装yaml
的最佳方法是通过pip
。 在windows终端上运行以下命令安装yaml,
# Windows
pip install pyaml
# *nix and Mac
sudo pip install pyaml
在运行上面的命令时,将根据当前的最新版本显示如下所示的内容。
Collecting pyaml
Using cached pyaml-17.12.1-py2.py3-none-any.whl
Collecting PyYAML (from pyaml)
Using cached PyYAML-3.12.tar.gz
Installing collected packages: PyYAML, pyaml
Running setup.py install for PyYAML ... done
Successfully installed PyYAML-3.12 pyaml-17.12.1
要测试它,转到Python shell并导入yaml模块,导入yaml
,如果没有提示错误,那么就是安装成功了。
安装pyaml后,让我们看下面的代码,文件:script_yaml1.py -
import yaml
mydict = {'a': 2, 'b': 3, 'c': 6}
mylist = [1, 2, 3, 4, 5, 6]
mytuple = ('a', 'b', 'c', 'x', 'y', 'z')
print(yaml.dump(mydict, default_flow_style=False))
print(yaml.dump(mylist, default_flow_style=False))
print(yaml.dump(mytuple, default_flow_style=False))
上面创建了三种不同的数据结构,字典,列表和元组。 在每个结构上,使用yaml.dump
。
执行上面示例代码后,得到以下结果 -
a: 2
b: 3
c: 6
- 1
- 2
- 3
- 4
- 5
- 6
!!python/tuple
- a
- b
- c
- x
- y
- z
字典输出看起来很干净,即它们的值。
用于分隔不同对象的空白区域。
列表标有破折号( - )
元组首先用!!python/tuple
表示,然后以与列表相同的格式表示。
加载yaml文件示例
所以,假设有一个yaml文件,如下所示 -
---
# An employee record
name: Raagvendra Joshi
job: Developer
skill: Oracle
employed: True
foods:
- Apple
- Orange
- Strawberry
- Mango
languages:
Oracle: Elite
power_builder: Elite
Full Stack Developer: Lame
education:
4 GCSEs
3 A-Levels
MCA in something called com
现在编写一个代码来通过yaml.load
函数加载这个yaml
文件。如下代码 -
import yaml
with open('data1.yaml') as f:
struct = yaml.load(f)
print(struct)
print('======================================')
print("To make the output in more readable format. add the json format.")
import json
print(json.dumps(struct, indent=4, separators=(',', ':')))
由于输出看起来不那么可读,最后通过使用json来美化它。 比较得到的输出结果和实际yaml文件。
{'name': 'Raagvendra Joshi', 'job': 'Developer', 'skill': 'Oracle', 'employed': True, 'foods': ['Apple', 'Orange', 'Strawberry', 'Mango'], 'languages': {'Oracle': 'Elite', 'power_builder': 'Elite', 'Full Stack Developer': 'Lame'}, 'education': '4 GCSEs 3 A-Levels MCA in something called com'}
======================================
To make the output in more readable format. add the json format.
{
"name":"Raagvendra Joshi",
"job":"Developer",
"skill":"Oracle",
"employed":true,
"foods":[
"Apple",
"Orange",
"Strawberry",
"Mango"
],
"languages":{
"Oracle":"Elite",
"power_builder":"Elite",
"Full Stack Developer":"Lame"
},
"education":"4 GCSEs 3 A-Levels MCA in something called com"
}
软件开发最重要的一个方面是调试。 在本节中,我们将看到使用内置调试器或第三方调试器进行Python调试的不同方法。
PDB - Python调试器
模块PDB支持设置断点。 断点是程序的故意暂停,您可以在其中获取有关程序状态的更多信息。
要设置断点,请插入该行 -
pdb.set_trace()
示例代码
pdb_example1.py
import pdb
x = 9
y = 7
pdb.set_trace()
total = x + y
pdb.set_trace()
在这个程序中插入了几个断点。 程序将在每个断点处暂停(pdb.set_trace()
)。 要查看变量内容,只需键入变量名称即可。
c:\Python\Python361>Python pdb_example1.py
> c:\Python\Python361\pdb_example1.py(8)<module>()
-> total = x + y
(Pdb) x
9
(Pdb) y
7
(Pdb) total
*** NameError: name 'total' is not defined
(Pdb)
按c
或继续执行程序直到下一个断点。
(Pdb) c
--Return--
> c:\Python\Python361\pdb_example1.py(8)<module>()->None
-> total = x + y
(Pdb) total
16
最终,需要调试更大的程序 - 使用子程序的程序。 有时候,试图找到的问题将存在于子程序中。 考虑以下程序。
import pdb
def squar(x, y):
out_squared = x^2 + y^2
return out_squared
if __name__ == "__main__":
#pdb.set_trace()
print (squar(4, 5))
现在运行上面的程序,
c:\Python\Python361>Python pdb_example2.py
> c:\Python\Python361\pdb_example2.py(10)<module>()
-> print (squar(4, 5))
(Pdb)
可以用问号(?
)获得帮助,但箭头表示即将执行的行。 在这一点上,按s
进入该行。
(Pdb) s
--Call--
>c:\Python\Python361\pdb_example2.py(3)squar()
-> def squar(x, y):
这是对函数的调用。 如果想要了解代码中的位置,请按l
-
(Pdb) l
1 import pdb
2
3 def squar(x, y):
4 -> out_squared = x^2 + y^2
5
6 return out_squared
7
8 if __name__ == "__main__":
9 pdb.set_trace()
10 print (squar(4, 5))
[EOF]
(Pdb)
可以点击n
进入下一行。 此时,位于out_squared
方法内,并且可以访问函数声明的变量。即:x
和y
。
(Pdb) x
4
(Pdb) y
5
(Pdb) x^2
6
(Pdb) y^2
7
(Pdb) x**2
16
(Pdb) y**2
25
(Pdb)
所以可以看到^
运算符不是我们想要的,而是需要使用**
运算符来计算正方形。
这样就可以在函数/方法中调试程序。
日志记录
自Python 2.3版以来,日志记录模块已成为Python标准库的一部分。 由于它是一个内置模块,所有Python模块都可以参与日志记录,因此应用程序日志可以包含自己的消息,该消息与来自第三方模块的消息集成在一起。 它提供了很多灵活性和功能。
日志记录的好处
- 诊断日志记录 - 它记录与应用程序操作相关的事件。
- 审核日志记录 - 它记录业务分析的事件。
消息以“严重性”和最小值的级别写入和记录
DEBUG (debug())
- 用于开发的诊断消息。INFO (info())
- 标准的“进度”消息。WARNING (warning())
- 检测到非严重问题。ERROR (error())
- 遇到错误,可能很严重。CRITICAL (critical())
- 通常是致命错误(程序停止)。
看看下面的简单程序,
import logging
logging.basicConfig(level=logging.INFO)
logging.debug('this message will be ignored') # This will not print
logging.info('This should be logged') # it'll print
logging.warning('And this, too') # It'll print
上面在严重性级别上记录消息。首先导入模块,调用basicConfig并设置日志记录级别。在上面设置的级别是INFO。 然后有三个不同的语句:debug
语句,info
语句和warning
语句。
上面代码的输出结果 -
INFO:root:This should be logged
WARNING:root:And this, too
由于info
语句低于debug
语句,我们无法看到调试消息。 要在Output终端中获取调试语句,需要更改的是basicConfig
级别。
logging.basicConfig(level = logging.DEBUG)
可以看到,如下所示的输出 -
DEBUG:root:this message will be ignored
INFO:root:This should be logged
WARNING:root:And this, too
此外,默认行为意味着如果不设置任何日志记录级别是警告。只需注释掉上面程序中的第二行并运行代码即可。
#logging.basicConfig(level = logging.DEBUG)
输出结果如下所示 -
WARNING:root:And this, too
内置日志记录级别的Python实际上是整数。
>>> import logging
>>>
>>> logging.DEBUG
10
>>> logging.CRITICAL
50
>>> logging.WARNING
30
>>> logging.INFO
20
>>> logging.ERROR
40
>>>
还可以将日志消息保存到文件中。如下代码指定保存的文件 -
logging.basicConfig(level = logging.DEBUG, filename = 'logging.log')
现在所有日志消息都将转到当前工作目录中的文件(logging.log
)而不是屏幕。 这是一个更好的方法,因为它允许对得到的消息进行后期分析。
还可以使用日志消息设置日期戳。
logging.basicConfig(level=logging.DEBUG, format = '%(asctime)s %(levelname)s:%(message)s')
输出会得到类似如下结果,
2018-03-08 19:30:00,066 DEBUG:this message will be ignored
2018-03-08 19:30:00,176 INFO:This should be logged
2018-03-08 19:30:00,201 WARNING:And this, too
基准测试
基准测试或分析基本上是为了测试代码的执行速度和瓶颈在哪里? 这样做的主要原因是为了优化。
timeit
Python附带了一个名为timeit
的内置模块。可以使用它来计算小代码片段的时间。 timeit模块使用特定于平台的时间函数,以便可以获得最准确的时序。
因此,它允许比较每个代码的两个代码,然后优化脚本以获得更好的性能。
timeit
模块具有命令行界面,但也可以导入。
调用脚本有两种方法。这里首先使用如何使用脚本,运行下面的代码并查看输出。
import timeit
print ( 'by index: ', timeit.timeit(stmt = "mydict['c']", setup = "mydict = {'a':5, 'b':10, 'c':15}", number = 1000000))
print ( 'by get: ', timeit.timeit(stmt = 'mydict.get("c")', setup = 'mydict = {"a":5, "b":10, "c":15}', number = 1000000))
执行上面示例代码,得到以下结果 -
by index: 0.1809192126703489
by get: 0.6088525265034692
上面使用两种不同的方法。 通过下标并获取访问字典键值。执行语句100万次,因为它对于非常小的数据执行得太快。 现在,与get
相比,可以看到索引访问更快。 可以多次运行代码,并且执行时间会略有不同,以便更好地理解。
另一种方法是在命令行中运行上述测试。如下 -
c:\Python\Python361>Python -m timeit -n 1000000 -s "mydict = {'a': 5, 'b':10, 'c':15}" "mydict['c']"
1000000 loops, best of 3: 0.187 usec per loop
c:\Python\Python361>Python -m timeit -n 1000000 -s "mydict = {'a': 5, 'b':10, 'c':15}" "mydict.get('c')"
1000000 loops, best of 3: 0.659 usec per loop
以上输出可能因系统硬件以及系统中当前运行的所有应用程序而异。
下面我们可以使用timeit
模块,如果想调用一个函数。 因为可以在函数内部添加多个语句来测试。
import timeit
def testme(this_dict, key):
return this_dict[key]
print (timeit.timeit("testme(mydict, key)", setup = "from __main__ import testme; mydict = {'a':9, 'b':18, 'c':27}; key = 'c'", number = 1000000))
执行上面示例代码,得到以下结果 -
0.7713474590139164