day5-python中的序列化與反序列化-json&pickle


一、概述

玩過稍微大型一點的游戲的朋友都知道,很多游戲的存檔功能使得我們可以方便地迅速進入上一次退出的狀態(包括裝備、等級、經驗值等在內的一切運行時數據),那么在程序開發中也存在這樣的需求:比較簡單的程序,對象的處理都在內存中直接實現,程序退出后對象就消失;但對於功能需求稍微拔高一點的程序來講,很多時候往往需要需要把對象持久化保存起來,以便下次啟動程序時還能直接進入最后一次的狀態。

這個處理過程在程序開發中就是序列化與反序列化。

二、序列化與反序列化的概念

概述中引入了一個游戲存檔的場景,本質上是游戲程序把運行時的對象轉換成可以持久存儲的對象,然后保存(到數據庫)的過程。還是以這個為引子來講講序列化與反序列化的概念(以下概念整合自網絡資料,個人認為解釋比較到位了)。

  • 序列化
    我們把程序運行時內存中的數據結構或對象轉換成二進制串字節序列的過程稱之為序列化,這樣我們就可以對對象實現持久化存儲或網絡傳輸。
    請注意以下重點:
    1. 序列化的對象
       
    內存中的數據結構或對象,也就是我們在程序運行中操縱的一切對象(這是一個面向對象的時代,當然包括很多地方說的變量啦~)
    2. 序列化后的對象
       
    變為二進制串字節序列,這個不作過多解釋,要持久化保存到硬件或進行網絡傳輸,必須是bytes對象。
    3. 序列化的目的
       
    想想自己玩游戲時存檔的那種便利性和必要性把,有些對象必須能夠持久化地保存(一般文件,數據庫,巴拉巴拉…)或進行網絡傳輸(分布式程序),才能滿足功能需求。
  • 反序列化
    反序列化就是序列化的逆向過程,持久化保存或網絡傳輸數據的最終目的也是為了后續使用,必須可以逆向加載到內存中二次利用,這就是反序列化。

python中的序列化與反序列化模塊有json和pickle,下面就來看看怎么玩轉它們。

三、json模塊

json模塊提供了dumps,loads,dump和load四種方法,下面展開來闡述:

  1. dumps序列化和loads反序列化
    dumps和loads是成對出現的:
    dumps用於將python中的簡單數據類型(典型的是字典和列表,還有字符串)進行json格式encode編碼,轉換為符合json格式的字符串(返回標准的json格式字符串);
    loads則剛好相反,用於把符合json格式的字符串decode成python中特定的數據類型(注意是簡單的數據類型,下文會詳細解釋)。
      1 >>> import json
      2 >>> list1=['a','b','c']
      3 >>> print(type(list))
      4 <class 'type'>
      5 
      6 #dumps序列化,可以理解為encode json過程
      7 >>> print(json.dumps(list1))
      8 ["a", "b", "c"]
      9 >>> print(type(json.dumps(list1)))
     10 <class 'str'>   #list dumps處理后變為str類型
     11 >>> dict1={'id':'001','name':'Maxwell'}
     12 >>> print(type(dict1))
     13 <class 'dict'>
     14 >>> print(type(json.dumps(dict1)))
     15 <class 'str'>   #dict經過dumps處理后也變成str類型
     16 >>>
     17 
     18 #loads反序列化
     19 >>> print(json.loads(json.dumps(list1)))
     20 ['a', 'b', 'c']
     21 >>> print(json.loads(json.dumps(dict1)))
     22 {'id': '001', 'name': 'Maxwell'}
     23 >>> print(type(json.loads(json.dumps(list1))))
     24 <class 'list'>  #把經過json dumps處理過的字符串loads序列化,可還原為原來的數據類型
     25 >>> print(type(json.loads(json.dumps(dict1))))
     26 <class 'dict'>  #把經過json dumps處理過的字符串loads序列化,可還原為原來的數據類型
    以上代碼僅僅是展示loads方法的效果,實際使用中我們可以把符合python中json格式的自定義字符串(比如程序中的輸入)轉換成特定的數據類型,這樣就可以在程序中跑起來:
      1 >>> str1='["a", "b", "c"]'
      2 >>> print(type(json.loads(str1)))
      3 <class 'list'>  #反序列化為list
      4 >>> print(json.loads(str1))
      5 ['a', 'b', 'c']
      6 >>> str2='{"id":"001","name":"Maxwell"}'
      7 >>> print(json.loads(str2))
      8 {'id': '001', 'name': 'Maxwell'}
      9 >>> print(json.loads(str2))
     10 {'id': '001', 'name': 'Maxwell'}
     11 >>> print(type(json.loads(str2)))
     12 <class 'dict'>  #反序列化為dict
    注意:通過loads方法反序列化自定義的字符串時,外層的引號必須是單引號,內層的引號是雙引號(可以對照dumps后輸出的引號是雙引號來看),這是python的規范,別問為什么了。

    好了,以上展示了dumps和loads的處理過程,我們的目的不是把python中的運行時對象能進行持久化保存或網絡傳輸嗎?下面展示通過dumps處理后保存到文本文件和從文本文件中loads出歷史保存的數據效果:

    (1) dumps序列化后保存
      1 import json
      2 dict1={'id':'001','name':'Maxwell'}
      3 with open('dumps.txt','w',encoding='utf-8') as f:
      4     f.write(json.dumps(dict1))
      5 
    看看保存后的文本文件dumps.txt的內容:
    image

    (2) loads反序列化從文件中加載數據
      1 >>> import json
      2 >>> with open('dumps.txt','r',encoding='utf-8') as f:
      3 ...     content = f.read()
      4 >>> print(json.loads(content))
      5 {'id': '001', 'name': 'Maxwell'}
      6 >>> print(type(json.loads(content)))
      7 <class 'dict'>  #loads反序列化后成功還原為原來的數據類型dict
      8 >>> print(json.loads(content).get('name'))
      9 Maxwell  #此時可以應用dict的各種大法了
     10 >>> print(json.loads(content)['name'])
     11 Maxwell
  2. dump序列化和load反序列化
    dump和load也是成對出現的,dump可以把python中的特定數據類型(比較多用的還是dict和list)轉換為json格式,並直接寫入寫入一個file-like Object(操作的對象包括未轉換的python對象和file-like Object),load則與此相反,可從文件對象中反序列化出python的原生對象出來。
      1 >>> import json
      2 >>> dict1={'id': '001', 'name': 'Maxwell'}
      3 >>> with open('dump.txt','w',encoding='utf-8') as f:
      4 ...     json.dump(dict1,f)   #dump序列化,直接操作原生數據類型對象和文件句柄
      5 ...
      6 
      7 #load反序列化
      8 >>> with open('dump.txt','r',encoding='utf-8') as f:
      9 ...     content = json.load(f)  #注意這里先用一個對象把load的內容保存起來,否則關閉文件后就不能再訪問了
     10 ...
     11 >>> print(content)
     12 {'id': '001', 'name': 'Maxwell'}
     13 >>> print(type(content))
     14 <class 'dict'>   #成功反序列化成dict
     15 >>> print(content['name'])
     16 Maxwell          #試試dict大法
  3. (dumps & loads) VS (dump & load)
    對比下dumps & loads和dump & load吧:
    (1)dumps & loads
           只能解決python中可以被json模塊處理的對象和json格式字符串之間相互轉換的問題,它們操作的對象分別是可以被json模塊處理的對象和json格式  
           字符串。
    (2)dump和load
            可以理解為json文件處理函數,操作的對象是python中可以被json模塊處理的對象和file-like Object,屏蔽或者說省略了json格式字符串這一細節
    綜合對比起來,如果要通過文件保存或從文件中加載運行時對象,使用dump和load更方便,代碼更少;反之如果僅僅需要進行json格式處理,則建議使用dumps和loads。

四、pickle

pickle模塊實現了用於對Python對象結構進行序列化和反序列化的二進制協議,與json模塊不同的是pickle模塊序列化和反序列化的過程分別叫做 pickling 和 unpickling,且轉換前后是二進制字節碼,不再是簡單的可閱讀的字符串:

  • pickling: 是將Python對象轉換為字節流的過程;
  • unpickling: 是將字節流二進制文件或字節對象轉換回Python對象的過程;
  1. dumps序列化和loads反序列化
    與jsonddumps和loads非常類似,不同的就在於轉換后的格式是二進制字節碼
      1 >>> import pickle
      2 >>> dict1={'id':'001','name':'Maxwell'}
      3 >>> pickle.dumps(dict1)
      4 b'\x80\x03}q\x00(X\x02\x00\x00\x00idq\x01X\x03\x00\x00\x00001q\x02X\x04\x00\x00\
      5 x00nameq\x03X\x07\x00\x00\x00Maxwellq\x04u.' #序列化成二進制字節碼
      6 >>> print(type(pickle.dumps(dict1)))
      7 <class 'bytes'>
      8 >>> pickle.loads(pickle.dumps(dict1))        #成功反序列化
      9 {'id': '001', 'name': 'Maxwell'}
     10 >>> print(type(pickle.loads(pickle.dumps(dict1))))
     11 <class 'dict'>
     12 >>> pickle.loads(pickle.dumps(dict1))['name']
     13 'Maxwell'
    由於pickle序列化后數據類型變為二進制字節碼,因此在保存文件和讀取文件時需要分別以wb和rb模式打開:
      1 >>> import pickle
      2 >>> dict1={'id':'001','name':'Maxwell'}
      3 >>> with open('picklt.txt','wb') as f:  #以wb模式打開文件后寫入dumps內容
      4 ...     f.write(pickle.dumps(dict1))
      5 ...
      6 
      7 >>> with open('picklt.txt','rb') as f:  #以rb模式打開后讀取內容
      8 ...     data = pickle.loads(f.read())
      9 ...
     10 >>> print(data)
     11 {'id': '001', 'name': 'Maxwell'}
    通過dumps寫入后,由於是二進制字節碼,所以打開會有亂碼顯示了:
    image
  2. dump序列化和load反序列化
    同理,pickle的dump序列化和load反序列化也和json的dump、load非常類似,還是看相同的例子吧:
      1 >>> import pickle
      2 >>> dict1={'id': '001', 'name': 'Maxwell'}
      3 >>> with open('pickle.txt','wb') as f:
      4 ...     pickle.dump(dict1,f)
      5 ...
      6 >>> with open('pickle.txt','rb') as f:
      7 ...     content = pickle.load(f)
      8 ...
      9 >>> print(content)
     10 {'id': '001', 'name': 'Maxwell'}
     11 >>> print(content['name'])
     12 Maxwell
    dump保存寫入的文件內容:
    image
  3. 序列化函數
    (1)序列化
      1 # !/usr/bin/env python
      2 # -*- coding: utf-8 -*-
      3 __author__ = 'Maxwell'
      4 
      5 import pickle
      6 
      7 def sayhi(name):
      8     print('Hello:', name)
      9 
     10 info = {'name':'Maxwell', 'func':sayhi}  #func對應的值是一個函數
     11 
     12 with open('test.txt', 'wb') as f:
     13     data = pickle.dumps(info)
     14     f.write(data)
     15 

    (2)反序列化
      1 # !/usr/bin/env python
      2 # -*- coding: utf-8 -*-
      3 __author__ = 'Maxwell'
      4 
      5 import pickle
      6 
      7 def sayhi(name):   #此處需要定義出函數,因為它不能被直接加載到內存中
      8     print('Hello:',name)
      9 
     10 with open('test.txt','rb') as f:
     11     data = pickle.loads(f.read())
     12 
     13 print(data.get('name'))
     14 data.get('func')('Tom')
     15 
     16 結果輸出:
     17 Maxwell
     18 Hello: Tom

四、json和pickle對比

  • JSON是一種文本序列化格式(它輸出的是unicode文件,大多數時候會被編碼為utf-8),而pickle是一個二進制序列化格式;
  • JOSN處理的是python對象和字符串的轉換問題,是我們可以讀懂的數據格式,而pickle是二進制格式,我們無法讀懂;
  • JSON是與特定的編程語言或系統無關的,且它在Python生態系統之外被廣泛使用,而pickle使用的數據格式是特定於Python的;
  • 默認情況下,JSON只能表示Python內建數據類型,而且是僅限於比較簡單的數據類型,如dict,list和str,對於自定義數據類型需要一些額外的工作來完成;pickle可以直接表示大量的Python數據類型,包括自定數據類型(其中,許多是通過巧妙地使用Python內省功能自動實現的;復雜的情況可以通過實現specific object API來解決)

以上內容摘自http://www.cnblogs.com/yyds/p/6563608.html



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM