【python】Python データシリアライズモジュール pickle 使用上の注意

Pickle は、Python でオブジェクト構造をシリアライズおよびデシリアライズするための Python 組み込みモジュールです。Python シリアライゼーションは、Python オブジェクト階層をバイト ストリームに変換するプロセスであり、ローカルに保存したり、ネットワーク経由で送信したりできます。デシリアライゼーションは、バイト ストリームを Python オブジェクト階層に復元するプロセスです。

データシリアライゼーションの機能は、ディスクに直接格納できないデータを格納し、それによってオブジェクトのライフサイクルを延長することと単純に理解されています。Python でよく使用されるシリアル化ライブラリには、json と pickle の 2 つがあります。json ライブラリと pickle ライブラリには主に 2 つの違いがあります。

  • Pickle は、通常はバイナリ ファイルとして保存されるクラスや関数を含む、Python のすべてのデータ型をシリアル化できます。また、json は Python の基本的なデータ型のみをシリアル化でき、ダンプ結果は非常に読みやすくなっています。
  • Pickle は Python でのみ使用できますが、json は異なる言語間でデータを交換できます。

特にデータ量が多い場合、Pickle は一般的に json よりも遅くなります。pickle と json の両方に 4 つの基本的なメソッドがあります。

方法 効果
ごみ ファイルへの書き込みをシリアライズする
ロード ファイルのデシリアライゼーションの読み取り
ダンプ 戻りオブジェクトをシリアライズする
荷重 オブジェクトをデシリアライズする

ピクルス1個分

pickle.dump() 関数は、Python 構造をシリアル化し、バイナリ ファイルとして保存するために使用されます。pickle.dump 関数は 3 つの引数を受け入れます。最初の引数にはファイルに保存されるオブジェクトが含まれ、2 番目の引数には目的のファイルをバイナリ モードで書き込むときに取得されるファイル オブジェクトが含まれます。3 番目のパラメーターは、シリアライゼーション プロトコルを示します。

pickle のプロトコル選択については、現在 5 つの異なるプロトコルが利用可能です ( Python オブジェクトのシリアライゼーションから)。使用するプロトコルが高いほど、生成された pickle を読み取るために必要な Python バージョンが新しくなります。これらの契約には以下が含まれます。

  • プロトコル バージョン 0 は、Python の以前のバージョンと下位互換性のある、元の「人間が判読できる」プロトコルです。
  • プロトコル バージョン 1 は古いバイナリ形式で、以前のバージョンの Python とも互換性があります。
  • プロトコル バージョン 2 は Python 2.3 で導入され、より効率的なシリアル化方法を提供します。
  • プロトコル バージョン 3 は Python 3.0 で導入されました。これは Python のデフォルト プロトコルでもあり、他の Python 3 バージョンとの互換性が必要な場合に推奨されるプロトコルでもあるバイト オブジェクトを明示的にサポートします。
  • プロトコル バージョン 4 は Python 3.4 で導入されました。非常に大きなオブジェクトのサポートを追加し、より多くの種類のオブジェクトをシリアル化し、いくつかのデータ形式を最適化します。

さまざまなプロトコルを 0 から 4 まで設定できます。プロトコル パラメータのデフォルトは None で、None は Python バージョンで使用されるデフォルト プロトコルを使用することを意味します。一致度が最も高い場合は -1 を選択します。さらに、プロトコルはそれぞれ定数を介して設定できます。

  • pickle.HIGHEST_PROTOCOL: 最高のプロトコルを示します。
  • pickle.DEFAULT_PROTOCOL: デフォルトのプロトコルを示します。
import pickle
print("当前python环境最高序列化协议版本为:{}".format(pickle.HIGHEST_PROTOCOL))
print("当前python环境默认序列化协议版本为:{}".format(pickle.DEFAULT_PROTOCOL))
当前python环境最高序列化协议版本为:4
当前python环境默认序列化协议版本为:3
# 序列化实例
import pickle
import numpy as np

data = {
    
    
    "name": "data struct",
    "number": 123.456,
    "tuple": ("first", False, 10.01),
    "numpy_data": np.ones((9,9),np.uint8)
}

# 保存到本地,这个文件名包含后缀可以随意命名,反正是二进制文件
with open('data.bin', 'wb') as f:
    # 设置最底层协议
    pickle.dump(data, f, 0)

# 查看文件大小
!du -h data.bin
print('---分界线---')
# 查看文件前十行,发现有可读文字
!cat data.bin | head -n 5
4.0K	data.bin
---分界线---
(dp0
Vname
p1
Vdata struct
p2
# 保存到本地,这个文件名包含后缀可以随意命名,反正是二进制文件
with open('data.bin', 'wb') as f:
    # 设置最底层协议
    pickle.dump(data, f, 1)

# 查看文件大小
!du -h data.bin
print('---分界线---')
# 查看文件前2行
!cat data.bin | head -n 2
4.0K	data.bin
---分界线---
}q (X   nameqX   data structqX   numberqG@^�/��wX   tupleq(X   firstqI00
G@$�Q�tqX
# 保存到本地,这个文件名包含后缀可以随意命名,反正是二进制文件
with open('data.bin', 'wb') as f:
    # 设置默认协议
    pickle.dump(data, f, pickle.DEFAULT_PROTOCOL)

# 查看文件大小
!du -h data.bin
print('---分界线---')
# 查看文件前2行
!cat data.bin | head -n 2
4.0K	data.bin
---分界线---
�}q (X   nameqX   data structqX   numberqG@^�/��wX   tupleqX   firstq�G@$�Q녇qX
   numpy_dataqcnumpy.core.multiarray
# 保存到本地,这个文件名包含后缀可以随意命名,反正是二进制文件
with open('data.bin', 'wb') as f:
    # 设置默认协议
    pickle.dump(data, f, 4)

# 查看文件大小
!du -h data.bin
print('---分界线---')
# 查看文件前2行
!cat data.bin | head -n 2
4.0K	data.bin
---分界线---
��/      }�(�name��data struct��number�G@^�/��w�tuple��first��G@$�Q녇��
numpy_data��numpy.core.multiarray��_reconstruct����numpy��ndarray���K ��Cb���R�(KK	K	��h�dtype����u1�����R�(K�|�NNNJ����J����K t�b�CQ�t�bu.

ファイルをデシリアライズして再読み込みしたい場合は、 pickle.load 関数を使用してください。シリアル化プロトコルは自動検出されるため、指定する必要はありません。さらに、現在の python バージョンより低いシリアライズされたファイルをデシリアライズする方法を pickle に伝えるための encoding と errors の 2 つのパラメータがあり、デフォルト値で問題ありません。

import pickle

with open('data.bin', 'rb') as f:
    data = pickle.load(f)
    print(type(data))
    print(data['name'])
    print(data.keys())
<class 'dict'>
data struct
dict_keys(['name', 'number', 'tuple', 'numpy_data'])

ファイルに書き込むのではなく、関数 dumps を使用して、オブジェクトのシリアル化された表現をバイト オブジェクトとして返します。bytes オブジェクトは、loads 関数によって逆シリアル化されます。bytes は Python3 の新しいタイプであり、bytes はデータをバイナリ形式で格納することのみを担当することに注意してください。

data = [1,2,3]

# 序列化,返回bytes对象
dumped = pickle.dumps(data)
print(dumped)
print(type(dumped))
print(len(dumped))

# 反序列化
loaded = pickle.loads(dumped)
print(loaded)
b'\x80\x03]q\x00(K\x01K\x02K\x03e.'
<class 'bytes'>
14
[1, 2, 3]

シリアライゼーションとデシリアライゼーションのプロセスは、__getstate__ および __setstate__ 関数の影響を受ける可能性があります。__getstate__ 関数はシリアライズ時に呼び出され、__setstate__ 関数はデシリアライズ時に呼び出されます。

例としては、シリアライズ時に一部のパラメータをシリアライズするように指定し、デシリアライズ時にパラメータを元に戻す場合の例です。

import pickle

class MyData:

    def __init__(self, x):

        self.x = x
        self.y = self.sqrt(x)

    def sqrt(self,x):
        return x**x

    def __getstate__(self):
        self.state = "ok"
        print("enter getstate")
        #  self.__dict__存储关于self.xxx的一些东西
        odict = self.__dict__.copy()
        del odict['y']
        print(odict)
        return odict

    def __setstate__(self, input):
        print("enter setstate")
        print(input)
        self.x = input['x']
        self.y = self.sqrt(self.x)

obj = MyData(3)
# 序列化
print("序列化")
dumped = pickle.dumps(obj)
# 反序列化
print("反序列化")
loaded = pickle.loads(dumped)
print("反序列化结果", loaded.y)
序列化
enter getstate
{'x': 3, 'state': 'ok'}
反序列化
enter setstate
{'x': 3, 'state': 'ok'}
反序列化结果 27

2 ピクルス加速

シリアル化するオブジェクトが特に大きい場合、シリアル化されたオブジェクトのピクルの読み込みと保存がコードのパフォーマンスのボトルネックになる可能性があります。pickle のシリアル化プロセスを高速化するには、一般に 3 つの方法があります。がある:

  • より高いプロトコル バージョンを使用する
  • ピクルスの代わりに cPickle を使用する
  • ガベージコレクターを無効にする

次の例はその使用方法を示していますが、高速化の効果は明ら​​かではありません。データ量が大きくないため、それをマークするコードを記述してください。

ピクルスを直接使用する

import time
import pickle
import numpy as np
import os 
def time_count(func):
    def inner(*args,**kwargs):
        start = time.time()
        func(*args,**kwargs)
        end = time.time()
        print('{}用时:{}秒'.format(func.__name__,end-start))
    return inner

@time_count
def pickle_dump(data,filepath):
    with open(filepath, 'wb') as f:
        pickle.dump(data, f)


@time_count
def pickle_load(filepath):
    with open(filepath, 'rb') as f:
        data = pickle.load(f)
    return data

data = np.ones((10000, 10000))
filepath = "file.dat"
pickle_dump(data,filepath)
pickle_load(filepath)
os.remove(filepath)
time.sleep(2)
pickle_dump用时:1.7647628784179688秒
pickle_load用时:1.7913622856140137秒

pickle の最高プロトコルを使用する

パラメーター protocol を -1 として指定できますが、加速は明らかではない場合があります。具体的にデータを見てください。

import time
import pickle
import numpy as np
import os

def time_count(func):
    def inner(*args,**kwargs):
        start = time.time()
        func(*args,**kwargs)
        end = time.time()
        print('{}用时:{}秒'.format(func.__name__,end-start))
    return inner

@time_count
def pickle_dump(data,filepath):
    with open(filepath, 'wb') as f:
        # 使用最高版本协议
        pickle.dump(data, f, -1)


@time_count
def pickle_load(filepath):
    with open(filepath, 'rb') as f:
        data = pickle.load(f)
    return data

data = np.ones((10000, 10000))
filepath = "file.dat"
pickle_dump(data,filepath)
pickle_load(filepath)
os.remove(filepath)
time.sleep(2)
pickle_dump用时:1.731525182723999秒
pickle_load用时:1.7664134502410889秒

ピクルスの代わりに cPickle を使用する

最も簡単な方法は、pickle の代わりに cPickle を使用することです。cPickle は pickle とまったく同じモジュールで、同じ関数と同じパラメーターを備えています。唯一の違いは、cPickle が C 言語で記述されているため、cPickle がはるかに高速になることです。

import time
# python3 导入cPickle方式
import _pickle as cPickle
import numpy as np
import os

def time_count(func):
    def inner(*args,**kwargs):
        start = time.time()
        func(*args,**kwargs)
        end = time.time()
        print('{}用时:{}秒'.format(func.__name__,end-start))
    return inner

@time_count
def pickle_dump(data,filepath):
    with open(filepath, 'wb') as f:
        # 使用最高版本协议
        cPickle.dump(data, f, -1)


@time_count
def pickle_load(filepath):
    with open(filepath, 'rb') as f:
        data = cPickle.load(f)
    return data

data = np.ones((10000, 10000))
filepath = "file.dat"
pickle_dump(data,filepath)
pickle_load(filepath)
os.remove(filepath)
time.sleep(2)
pickle_dump用时:1.7443737983703613秒
pickle_load用时:1.7894999980926514秒

ガベージ コレクションを無効にする

ガベージ コレクターは処理を遅くするため、無効にするとパフォーマンスが向上します。

import time
import pickle
import numpy as np
import os
import gc

# 禁用垃圾回收
gc.disable()

def time_count(func):
    def inner(*args,**kwargs):
        start = time.time()
        func(*args,**kwargs)
        end = time.time()
        print('{}用时:{}秒'.format(func.__name__,end-start))
    return inner

@time_count
def pickle_dump(data,filepath):
    with open(filepath, 'wb') as f:
        # 使用最高版本协议
        pickle.dump(data, f, -1)


@time_count
def pickle_load(filepath):
    with open(filepath, 'rb') as f:
        data = pickle.load(f)
    return data

data = np.ones((10000, 10000))
filepath = "file.dat"
pickle_dump(data,filepath)
pickle_load(filepath)
os.remove(filepath)
time.sleep(2)

# 开启垃圾回收
gc.enable()
pickle_dump用时:1.8271889686584473秒
pickle_load用时:1.7800366878509521秒

3 参考

おすすめ

転載: blog.csdn.net/LuohenYJ/article/details/125669231