最近、クローラーによって取得されたダウンロード結果をアーカイブしたいと考えています。この結果は Python オブジェクトです (単純に HTML または json を保存するのではなく、ダウンロード プロセス全体をそこで、Python の組み込みの pickle ライブラリ (キュウリのピクルス ライブラリ) を使用して、オブジェクトをバイトにシリアル化し、必要に応じて逆シリアル化することを考えました。
次のコードを通じて、pickle の使用法と機能を簡単に理解できます。
In [2]: import pickle In [3]: class A: pass In [4]: a = A() In [5]: a.foo = 'hello' In [6]: a.bar = 2 In [7]: pick_ed = pickle.dumps(a) In [8]: pick_ed Out[8]: b'\x80\x03c__main__\nA\nq\x00)\x81q\x01}q\x02(X\x03\x00\x00\x00fooq\x03X\x05\x00\x00\x00helloq\x04X\x03\x00\x00\x00barq\x05K\x02ub.' In [9]: unpick = pickle.loads(pick_ed) In [10]: unpick Out[10]: <__main__.A at 0x10ae67278> In [11]: a Out[11]: <__main__.A at 0x10ae67128> In [12]: dir(unpick) Out[12]: ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slotnames__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo'] In [13]: unpick.foo Out[13]: 'hello' In [14]: unpick.bar Out[14]: 2
pickle の使用法は json に似ていることがわかりますが、いくつかの根本的な違いがあります。
json は言語を超えたユニバーサル データ交換形式であり、通常はテキストで表現されます。人間は読める。 pickle は Python オブジェクトをシリアル化するために使用されます (Python のみ)。シリアル化の結果はバイナリ データであり、人間には読み取れません。さらに、json はデフォルトで組み込み型の一部しかシリアル化できませんが、pickle はかなり多くのデータをシリアル化できます。
古代の元帥も組み込まれています。ただし、このライブラリは主に .pyc ファイル用です。カスタム タイプはサポートされておらず、完全ではありません。たとえば、循環アプリケーションを処理できないため、オブジェクトがそれ自体を参照している場合、マーシャルを使用すると Python インタープリタがハングします。
バージョンの互換性の問題
pickle は Python 用であるため、Python には異なるバージョン (2 と 3 の違いは非常に大きい) があるため、それを考慮する必要があります。考慮事項 シリアル化されたオブジェクトは、Python の上位 (または下位) バージョンで逆シリアル化できます。
現在、pickle プロトコルのバージョンは 5 つあります。バージョンが高いほど、Python のバージョンも高くなります。0 ~ 2 は Python2 用で、3 ~ 4 は Python3 用です。
Protocol version 0 is the original “human-readable” protocol and is backwards compatible with earlier versions of Python.Protocol version 1 is an old binary format which is also compatible with earlier versions of Python.Protocol version 2 was introduced in Python 2.3. It provides much more efficient pickling of new-style classes. Refer to PEP 307for information about improvements brought by protocol 2. (从这个版本往后,性能有显著提高)Protocol version 3 was added in Python 3.0. It has explicit support for bytes objects and cannot be unpickled by Python 2.x.This is the default protocol, and the recommended protocol when compatibility with other Python 3 versions is required.Protocol version 4 was added in Python 3.4. It adds support for very large objects, pickling more kinds of objects, and some data format optimizations. Refer to PEP 3154 for information about improvements brought byprotocol 4.
ほとんどの入り口pickle 関数 (dump()、dumps()、Pickler コンストラクターなど) はすべて、2 つの組み込み変数を持つプロトコル バージョン パラメーターを受け入れます:
pickle.HIGHEST_PROTOCOL は現在 4
pickle DEFAULT_PROTOCOL は現在 3
Usage
は組み込みの JSON モジュール インターフェイスに似ており、dumps() はシリアル化結果を返すために使用され、dump() は使用されますシリアライズしてからファイルをインポートします。同様にload()とloads()もあります。このうち、ダンプをシリアライズする際にはプロトコルのバージョンを指定できますが、デシリアライズ時には指定する必要はなく、自動的にバージョンが識別されます。これは zip コマンドとよく似ています。
組み込み型のシリアル化
ほとんどの組み込み型はシリアル化と逆シリアル化をサポートしています。特に注意が必要なのは関数です。関数のシリアル化は、その名前とそれが配置されているモジュールにのみ基づいています。関数のコードもその属性 (Python の関数はファーストクラスのオブジェクトであり、属性を持つことができます) もシリアル化されません。これには、関数が配置されているモジュールが unpickle 環境でインポート可能である必要があります。そうしないと、ImportError または AttributeError が発生します。
ここで興味深い点があります。すべてのラムダ関数は Pickleable ではありません。それらの名前は
カスタム型のシリアル化
この記事の冒頭の実験コードと同様、ほとんどの場合、追加の操作は不要です。この操作によりシリアル化/逆シリアル化操作を実現できます。 。逆シリアル化プロセス中、オブジェクトを初期化するためにクラスの __init__() は呼び出されませんが、初期化されていない新しいインスタンスが作成され、その属性が復元されることに注意してください (非常に賢明です)。疑似コードは次のとおりです。
def save(obj): return (obj.__class__, obj.__dict__) def load(cls, attributes): obj = cls.__new__(cls) obj.__dict__.update(attributes) return obj
オブジェクトの状態の保存など、シリアル化プロセス中に追加の操作を実行したい場合は、ピクル プロトコルの最も一般的なメソッドであるマジック メソッドを使用できます。 __setstate__() と __getstate__() です。
セキュリティの問題 (!)
pickle ドキュメントの冒頭には、「未知のソースからバイナリを決して unpickle しないでください」と書かれています。次のコードを考えてみましょう。
>>> import pickle >>> pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.") hello world 0
このコードがアンピックされると、os.system() がインポートされてから、echo が呼び出されます。副作用はありません。しかし、rm -rf /· の場合はどうなるでしょうか?
ドキュメントで示されている提案は、Unpickler.find_class() にチェック ロジックを実装することです。グローバル変数が必要な場合は、関数メソッドを呼び出す必要があります。
import builtins import io import pickle safe_builtins = { 'range', 'complex', 'set', 'frozenset', 'slice', } class RestrictedUnpickler(pickle.Unpickler): def find_class(self, module, name): # Only allow safe classes from builtins. if module == "builtins" and name in safe_builtins: return getattr(builtins, name) # Forbid everything else. raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name)) def restricted_loads(s): """Helper function analogous to pickle.loads().""" return RestrictedUnpickler(io.BytesIO(s)).load()
圧縮
ピクル後に自動的に圧縮されません。この設計は非常に良いと思います。分離されています。ピクルは単なるピクルです. 圧縮は他のライブラリに任せています。また、ピクル後のファイルは読めないものの、内容は依然として ASCII コードで表示されており、文字化けしていないことにも気づくでしょう。圧縮ライブラリの compress を呼び出す必要があります。実際に圧縮すると、ボリュームは以前の約 1/3 になり、非常に印象的です。
概要
グローバル変数をインポート可能にしておくのは少し難しいです。私が直面しなければならない質問は、今日漬けたものを将来開ける必要がある場合でも、開けることができるかということです。
ここには、プロジェクト バージョン、Python バージョン、pickle プロトコル バージョン、プロジェクトが依存するパッケージ バージョンなど、いくつかのバージョンがあります。その中でも、Python 版と pickle 版は下位互換性を信頼でき、解決しやすいと思います。主にプロジェクト、バージョン、および依存バージョンです。選択するオブジェクトが非常に複雑な場合は、古いバージョンのバックアップが新しいバージョンと互換性がない可能性があります。考えられる解決策は、ハッシュ値を記録するなど、すべての依存関係を完全にロックすることです。特定のバイナリ シーケンスを復元する場合は、その時点でのプロジェクトの特定の依存関係と特定のコミットを復元します。
しかし、今のところ、私たちの要件は基本的に、requests.Response オブジェクトをピクルすることです。下位互換性を信頼できると思います。ある日リクエストに重大な変更があった場合、たとえピクルに互換性があったとしても、コードには互換性がなくなり、そのときは他の戦略が考慮されます。
以上がPython の組み込み pickle ライブラリのオブジェクトのシリアル化と逆シリアル化の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。