在數(shù)據(jù)存儲(chǔ)的上下文中,序列化是將數(shù)據(jù)結(jié)構(gòu)或?qū)ο鬆顟B(tài)轉(zhuǎn)換為可以存儲(chǔ)(例如,在文件或存儲(chǔ)緩沖器中)或稍后傳輸和重構(gòu)的格式的過程。
在序列化中,對(duì)象被轉(zhuǎn)換為可以存儲(chǔ)的格式,以便以后能夠?qū)ζ溥M(jìn)行反序列化并從序列化格式重新創(chuàng)建原始對(duì)象。
Pickling是將Python對(duì)象層次結(jié)構(gòu)轉(zhuǎn)換為要寫入文件的字節(jié)流(通常不是人類可讀的)的過程,這也稱為序列化。Unpickling是反向操作,將字節(jié)流轉(zhuǎn)換回工作的Python對(duì)象層次結(jié)構(gòu)。
Pickle是操作上最簡(jiǎn)單的存儲(chǔ)對(duì)象的方法。 Python Pickle模塊是一種面向?qū)ο蟮姆绞?,可以直接以特殊的存?chǔ)格式存儲(chǔ)對(duì)象。
它能做什么?
Pickle不能做什么?
簡(jiǎn)而言之,pickling是一種在文件中存儲(chǔ)和檢索數(shù)據(jù)變量的方法,其中變量可以是列表,類等。
要使用Pickle,必須要 -
Pickle例如
pickle.dump(mystring, outfile, protocol),
其中第三個(gè)參數(shù):protocol是可選的,
要使用unpickling,必須要 -
Pickle例如
myString = pickle.load(inputfile)
pickle 接口提供四種不同的方法。
dump() - 序列化到打開的文件(類文件對(duì)象)。dumps() - 序列化為字符串load() - 從類似開放的對(duì)象反序列化。loads() - 從字符串反序列化。基于以上程序,下面是“pickle”的一個(gè)例子。
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)
執(zhí)行上面示例代碼,得到以下結(jié)果 -
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.'
因此,在上面的示例中,創(chuàng)建了一個(gè)Cat類的實(shí)例,然后將它pickled,將“Cat”實(shí)例轉(zhuǎn)換為一個(gè)簡(jiǎn)單的字節(jié)數(shù)組。
這樣,可以輕松地將字節(jié)數(shù)組存儲(chǔ)在二進(jìn)制文件或數(shù)據(jù)庫字段中,并在以后從存儲(chǔ)支持將其恢復(fù)為原始格式。
此外,如果要?jiǎng)?chuàng)建帶有pickle對(duì)象的文件,可以使用dump()方法(而不是dumps *()* one)同時(shí)傳遞打開的二進(jìn)制文件,并且pickling結(jié)果將自動(dòng)存儲(chǔ)在文件中。
[….]
binary_file = open(my_pickled_Pussy.bin', mode='wb')
my_pickled_Pussy = pickle.dump(Pussy, binary_file)
binary_file.close()
采用二進(jìn)制數(shù)組并將其轉(zhuǎn)換為對(duì)象層次結(jié)構(gòu)的過程稱為unpickling。
通過使用pickle模塊的load()函數(shù)完成unpickling過程,并從簡(jiǎn)單的字節(jié)數(shù)組返回一個(gè)完整的對(duì)象層次結(jié)構(gòu)。
在前面的例子中使用load函數(shù)。
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))
執(zhí)行上面示例代碼,得到以下結(jié)果 -
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(JavaScript Object Notation)已經(jīng)成為Python標(biāo)準(zhǔn)庫的一部分,是一種輕量級(jí)的數(shù)據(jù)交換格式。 人類很容易讀寫。 它很容易解析和生成。
由于其簡(jiǎn)單性,JSON是我們存儲(chǔ)和交換數(shù)據(jù)的一種方式,它通過其JSON語法實(shí)現(xiàn),并在許多Web應(yīng)用程序中使用。 因?yàn)樗侨祟惪勺x的格式,這可能是在數(shù)據(jù)傳輸中使用它的原因之一,除了它在使用API時(shí)的有效性。
JSON格式數(shù)據(jù)的示例如下 -
{"EmployID": 40203, "Name": "Maxsu", "Age":54, "isEmployed": True}
Python使用的模塊是JSON模塊使得使用Json文件變得簡(jiǎn)單。應(yīng)該在Python安裝中包含(內(nèi)置)此模塊。
下面來看看如何將Python字典轉(zhuǎn)換為JSON并將其寫入文本文件。
JSON到Python
讀JSON意味著將JSON轉(zhuǎn)換為Python值(對(duì)象)。 json庫將JSON解析為Python中的字典或列表。要做到這一點(diǎn),可使用loads()函數(shù)(從字符串加載),如下所示 -
import json
json_data = '{"EmployeeID": 10010, "EmployeeName": "Maxsu", "Department":"技術(shù)部"}'
json2python = json.loads(json_data)
執(zhí)行上面示例代碼,得到以下結(jié)果 -
E:\worksp\pycharm\venv\Scripts\python.exe E:/worksp/pycharm/main.py
{'EmployeeID': 10010, 'EmployeeName': 'Maxsu', 'Department': '技術(shù)部'}
下面是一個(gè)示例json文件:data1.json,
{"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}}
上面的內(nèi)容(data1.json)看起來像傳統(tǒng)的字典。 可以使用pickle來存儲(chǔ)這個(gè)文件,但它的輸出不是人類可讀的形式。
JSON(Java Script Object Notification)是一種非常簡(jiǎn)單的格式,這也是它受歡迎的原因之一。 現(xiàn)在通過以下程序來打印上面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)
執(zhí)行上面示例代碼,得到以下結(jié)果 -
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)進(jìn)行讀取,獲取文件處理程序并傳遞給json.load并獲取對(duì)象。 當(dāng)打印對(duì)象的輸出時(shí),可以看到它與json文件相同。 雖然對(duì)象的類型是字典,它此時(shí)是Python對(duì)象。上面加載json文件,添加另一個(gè)鍵值對(duì)并將其寫回到到另一個(gè)json文件中。 現(xiàn)在,如果打開文件:data2.json,它看起來不同。與之前看到的格式不同。
要想使輸出看起來相同(人類可讀的格式),將幾個(gè)參數(shù)添加到程序的最后一行,參考如上代碼 -
json.dump(conf, fh, indent = 4, separators = (',', ': '))
與pickle類似,可以使用轉(zhuǎn)儲(chǔ)打印字符串并使用load加載。 以下是一個(gè)例子,

YAML可能是所有編程語言中最人性化的數(shù)據(jù)序列化標(biāo)準(zhǔn)。
Python yaml模塊叫作:pyaml
YAML是JSON的替代品 -
&)和別名(*)。由于yaml不是內(nèi)置模塊,需要手動(dòng)安裝它。 在Windows機(jī)器上安裝yaml的最佳方法是通過pip。 在windows終端上運(yùn)行以下命令安裝yaml,
# Windows
pip install pyaml
# *nix and Mac
sudo pip install pyaml
在運(yùn)行上面的命令時(shí),將根據(jù)當(dāng)前的最新版本顯示如下所示的內(nèi)容。
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
要測(cè)試它,轉(zhuǎn)到Python shell并導(dǎo)入yaml模塊,導(dǎo)入yaml,如果沒有提示錯(cuò)誤,那么就是安裝成功了。
安裝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))
上面創(chuàng)建了三種不同的數(shù)據(jù)結(jié)構(gòu),字典,列表和元組。 在每個(gè)結(jié)構(gòu)上,使用yaml.dump。
執(zhí)行上面示例代碼后,得到以下結(jié)果 -
a: 2
b: 3
c: 6
- 1
- 2
- 3
- 4
- 5
- 6
!!python/tuple
- a
- b
- c
- x
- y
- z
字典輸出看起來很干凈,即它們的值。
用于分隔不同對(duì)象的空白區(qū)域。
列表標(biāo)有破折號(hào)( - )
元組首先用!!python/tuple表示,然后以與列表相同的格式表示。
加載yaml文件示例
所以,假設(shè)有一個(gè)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
現(xiàn)在編寫一個(gè)代碼來通過yaml.load函數(shù)加載這個(gè)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來美化它。 比較得到的輸出結(jié)果和實(shí)際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"
}
軟件開發(fā)最重要的一個(gè)方面是調(diào)試。 在本節(jié)中,我們將看到使用內(nèi)置調(diào)試器或第三方調(diào)試器進(jìn)行Python調(diào)試的不同方法。
模塊PDB支持設(shè)置斷點(diǎn)。 斷點(diǎn)是程序的故意暫停,您可以在其中獲取有關(guān)程序狀態(tài)的更多信息。
要設(shè)置斷點(diǎn),請(qǐng)插入該行 -
pdb.set_trace()
示例代碼
pdb_example1.py
import pdb
x = 9
y = 7
pdb.set_trace()
total = x + y
pdb.set_trace()
在這個(gè)程序中插入了幾個(gè)斷點(diǎn)。 程序?qū)⒃诿總€(gè)斷點(diǎn)處暫停(pdb.set_trace())。 要查看變量?jī)?nèi)容,只需鍵入變量名稱即可。
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或繼續(xù)執(zhí)行程序直到下一個(gè)斷點(diǎn)。
(Pdb) c
--Return--
> c:\Python\Python361\pdb_example1.py(8)<module>()->None
-> total = x + y
(Pdb) total
16
最終,需要調(diào)試更大的程序 - 使用子程序的程序。 有時(shí)候,試圖找到的問題將存在于子程序中。 考慮以下程序。
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))
現(xiàn)在運(yùn)行上面的程序,
c:\Python\Python361>Python pdb_example2.py
> c:\Python\Python361\pdb_example2.py(10)<module>()
-> print (squar(4, 5))
(Pdb)
可以用問號(hào)(?)獲得幫助,但箭頭表示即將執(zhí)行的行。 在這一點(diǎn)上,按s進(jìn)入該行。
(Pdb) s
--Call--
>c:\Python\Python361\pdb_example2.py(3)squar()
-> def squar(x, y):
這是對(duì)函數(shù)的調(diào)用。 如果想要了解代碼中的位置,請(qǐng)按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)
可以點(diǎn)擊n進(jìn)入下一行。 此時(shí),位于out_squared方法內(nèi),并且可以訪問函數(shù)聲明的變量。即: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)
所以可以看到^運(yùn)算符不是我們想要的,而是需要使用**運(yùn)算符來計(jì)算正方形。
這樣就可以在函數(shù)/方法中調(diào)試程序。
自Python 2.3版以來,日志記錄模塊已成為Python標(biāo)準(zhǔn)庫的一部分。 由于它是一個(gè)內(nèi)置模塊,所有Python模塊都可以參與日志記錄,因此應(yīng)用程序日志可以包含自己的消息,該消息與來自第三方模塊的消息集成在一起。 它提供了很多靈活性和功能。
日志記錄的好處
消息以“嚴(yán)重性”和最小值的級(jí)別寫入和記錄
DEBUG (debug()) - 用于開發(fā)的診斷消息。INFO (info()) - 標(biāo)準(zhǔn)的“進(jìn)度”消息。WARNING (warning()) - 檢測(cè)到非嚴(yán)重問題。ERROR (error()) - 遇到錯(cuò)誤,可能很嚴(yán)重。CRITICAL (critical()) - 通常是致命錯(cuò)誤(程序停止)。看看下面的簡(jiǎn)單程序,
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
上面在嚴(yán)重性級(jí)別上記錄消息。首先導(dǎo)入模塊,調(diào)用basicConfig并設(shè)置日志記錄級(jí)別。在上面設(shè)置的級(jí)別是INFO。 然后有三個(gè)不同的語句:debug語句,info語句和warning語句。
上面代碼的輸出結(jié)果 -
INFO:root:This should be logged
WARNING:root:And this, too
由于info語句低于debug語句,我們無法看到調(diào)試消息。 要在Output終端中獲取調(diào)試語句,需要更改的是basicConfig級(jí)別。
logging.basicConfig(level = logging.DEBUG)
可以看到,如下所示的輸出 -
DEBUG:root:this message will be ignored
INFO:root:This should be logged
WARNING:root:And this, too
此外,默認(rèn)行為意味著如果不設(shè)置任何日志記錄級(jí)別是警告。只需注釋掉上面程序中的第二行并運(yùn)行代碼即可。
#logging.basicConfig(level = logging.DEBUG)
輸出結(jié)果如下所示 -
WARNING:root:And this, too
內(nèi)置日志記錄級(jí)別的Python實(shí)際上是整數(shù)。
>>> 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')
現(xiàn)在所有日志消息都將轉(zhuǎn)到當(dāng)前工作目錄中的文件(logging.log)而不是屏幕。 這是一個(gè)更好的方法,因?yàn)樗试S對(duì)得到的消息進(jìn)行后期分析。
還可以使用日志消息設(shè)置日期戳。
logging.basicConfig(level=logging.DEBUG, format = '%(asctime)s %(levelname)s:%(message)s')
輸出會(huì)得到類似如下結(jié)果,
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
基準(zhǔn)測(cè)試或分析基本上是為了測(cè)試代碼的執(zhí)行速度和瓶頸在哪里? 這樣做的主要原因是為了優(yōu)化。
timeit
Python附帶了一個(gè)名為timeit的內(nèi)置模塊。可以使用它來計(jì)算小代碼片段的時(shí)間。 timeit模塊使用特定于平臺(tái)的時(shí)間函數(shù),以便可以獲得最準(zhǔn)確的時(shí)序。
因此,它允許比較每個(gè)代碼的兩個(gè)代碼,然后優(yōu)化腳本以獲得更好的性能。
timeit模塊具有命令行界面,但也可以導(dǎo)入。
調(diào)用腳本有兩種方法。這里首先使用如何使用腳本,運(yùn)行下面的代碼并查看輸出。
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))
執(zhí)行上面示例代碼,得到以下結(jié)果 -
by index: 0.1809192126703489
by get: 0.6088525265034692
上面使用兩種不同的方法。 通過下標(biāo)并獲取訪問字典鍵值。執(zhí)行語句100萬次,因?yàn)樗鼘?duì)于非常小的數(shù)據(jù)執(zhí)行得太快。 現(xiàn)在,與get相比,可以看到索引訪問更快。 可以多次運(yùn)行代碼,并且執(zhí)行時(shí)間會(huì)略有不同,以便更好地理解。
另一種方法是在命令行中運(yùn)行上述測(cè)試。如下 -
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
以上輸出可能因系統(tǒng)硬件以及系統(tǒng)中當(dāng)前運(yùn)行的所有應(yīng)用程序而異。
下面我們可以使用timeit模塊,如果想調(diào)用一個(gè)函數(shù)。 因?yàn)榭梢栽诤瘮?shù)內(nèi)部添加多個(gè)語句來測(cè)試。
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))
執(zhí)行上面示例代碼,得到以下結(jié)果 -
0.7713474590139164