Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Pensirilan objek Python dan penyahserikatan: Bahagian 2

Pensirilan objek Python dan penyahserikatan: Bahagian 2

PHPz
PHPzasal
2023-09-03 20:33:051378semak imbas

Python 对象序列化和反序列化:第 2 部分

Ini ialah bahagian kedua tutorial tentang mensiri dan menyahsiri objek Python. Pada bahagian pertama, anda mempelajari asas-asas dan kemudian menyelidiki butiran Pickle dan JSON.

Dalam bahagian ini, anda akan meneroka YAML (pastikan anda mempunyai contoh berjalan dari bahagian satu), membincangkan prestasi dan pertimbangan keselamatan, belajar tentang format bersiri lain, dan akhirnya belajar cara memilih yang betul. p>

YAML

YAML ialah format kegemaran saya. Ia adalah format siri data mesra manusia. Tidak seperti Pickle dan JSON, ia bukan sebahagian daripada perpustakaan standard Python, jadi anda perlu memasangnya:

pip install yamlpip 安装 yaml

yaml模块只有load()dump()函数。默认情况下,它们使用像 loads()dumps() 这样的字符串,但可以采用第二个参数,它是一个开放流,然后可以转储/加载到/来自文件。

import yaml



print yaml.dump(simple)



boolean: true

int_list: [1, 2, 3]

none: null

number: 3.44

text: string

请注意 YAML 与 Pickle 甚至 JSON 相比的可读性。现在是 YAML 最酷的部分:它理解 Python 对象!无需自定义编码器和解码器。以下是使用 YAML 的复杂序列化/反序列化:

> serialized = yaml.dump(complex)

> print serialized



a: !!python/object:__main__.A

  simple:

    boolean: true

    int_list: [1, 2, 3]

    none: null

    number: 3.44

    text: string

when: 2016-03-07 00:00:00



> deserialized = yaml.load(serialized)

> deserialized == complex

True

如您所见,YAML 有自己的符号来标记 Python 对象。输出仍然非常容易阅读。日期时间对象不需要任何特殊标记,因为 YAML 本质上支持日期时间对象。

性能

在开始考虑性能之前,您需要考虑性能是否是一个问题。如果您相对不频繁地序列化/反序列化少量数据(例如在程序开始时读取配置文件),那么性能并不是真正的问题,您可以继续前进。

但是,假设您分析了系统并发现序列化和/或反序列化导致性能问题,则需要解决以下问题。

性能有两个方面:序列化/反序列化的速度有多快,以及序列化表示有多大?

为了测试各种序列化格式的性能,我将创建一个较大的数据结构,并使用 Pickle、YAML 和 JSON 对其进行序列化/反序列化。 big_data 列表包含 5,000 个复杂对象。

big_data = [dict(a=simple, when=datetime.now().replace(microsecond=0)) for i in range(5000)]

泡菜

我将在这里使用 IPython,因为它有方便的 %timeit 魔术函数来测量执行时间。

import cPickle as pickle



In [190]: %timeit serialized = pickle.dumps(big_data)

10 loops, best of 3: 51 ms per loop



In [191]: %timeit deserialized = pickle.loads(serialized)

10 loops, best of 3: 24.2 ms per loop



In [192]: deserialized == big_data

Out[192]: True



In [193]: len(serialized)

Out[193]: 747328

默认pickle需要83.1毫秒进行序列化,29.2毫秒进行反序列化,序列化大小为747,328字节。

让我们尝试使用最高协议。

In [195]: %timeit serialized = pickle.dumps(big_data, protocol=pickle.HIGHEST_PROTOCOL)

10 loops, best of 3: 21.2 ms per loop



In [196]: %timeit deserialized = pickle.loads(serialized)

10 loops, best of 3: 25.2 ms per loop



In [197]: len(serialized)

Out[197]: 394350

有趣的结果。序列化时间缩短至仅 21.2 毫秒,但反序列化时间略有增加,达到 25.2 毫秒。序列化大小显着缩小至 394,350 字节 (52%)。

JSON

In [253] %timeit serialized = json.dumps(big_data, cls=CustomEncoder)

10 loops, best of 3: 34.7 ms per loop



In [253] %timeit deserialized = json.loads(serialized, object_hook=decode_object)

10 loops, best of 3: 148 ms per loop



In [255]: len(serialized)

Out[255]: 730000

好的。编码方面的性能似乎比 Pickle 差一点,但解码方面的性能却差很多很多:慢了 6 倍。这是怎么回事?这是 object_hook 函数的一个工件,需要为每个字典运行以检查是否需要将其转换为对象。不使用对象挂钩运行速度要快得多。

%timeit deserialized = json.loads(serialized)

10 loops, best of 3: 36.2 ms per loop

这里的教训是,在序列化和反序列化为 JSON 时,请仔细考虑任何自定义编码,因为它们可能会对整体性能产生重大影响。

YAML

In [293]: %timeit serialized = yaml.dump(big_data)

1 loops, best of 3: 1.22 s per loop



In[294]: %timeit deserialized = yaml.load(serialized)

1 loops, best of 3: 2.03 s per loop



In [295]: len(serialized)

Out[295]: 200091

好的。 YAML 真的非常非常慢。但是,请注意一些有趣的事情:序列化大小仅为 200,091 字节。比 Pickle 和 JSON 都好得多。让我们快速了解一下内部:

In [300]: print serialized[:211]

- a: &id001

    boolean: true

    int_list: [1, 2, 3]

    none: null

    number: 3.44

    text: string

  when: 2016-03-13 00:11:44

- a: *id001

  when: 2016-03-13 00:11:44

- a: *id001

  when: 2016-03-13 00:11:44

YAML 在这里非常聪明。它确定所有 5,000 个字典共享相同的“a”键值,因此它仅存储一次并使用 *id001 为所有对象引用它。

安全

安全性通常是一个关键问题。 Pickle和YAML由于构造Python对象,很容易受到代码执行攻击。格式巧妙的文件可以包含将由 Pickle 或 YAML 执行的任意代码。无需惊慌。这是设计使然,并记录在 Pickle 的文档中:

警告:pickle 模块并非旨在防止错误或恶意构建的数据。切勿取消从不受信任或未经身份验证的来源收到的数据。

以及 YAML 文档中的内容:

警告:使用从不受信任的来源收到的任何数据调用 yaml.load 是不安全的! yaml.load 与 pickle.load 一样强大,因此可以调用任何 Python 函数。

您只需要了解,不应使用 Pickle 或 YAML 加载从不受信任的来源收到的序列化数据。 JSON 没问题,但是如果您有自定义编码器/解码器,那么您也可能会暴露。

yaml 模块提供了 yaml.safe_load()

Modul yaml hanya mempunyai fungsi load() dan dump(). Secara lalai mereka menggunakan rentetan seperti loads() dan dumps() tetapi boleh mengambil hujah kedua , iaitu strim terbuka yang kemudiannya boleh dibuang/dimuat ke/daripada fail. 🎜 rrreee 🎜Sila ambil perhatian betapa mudah dibaca YAML dibandingkan dengan Pickle atau JSON. Sekarang datang bahagian yang menarik tentang YAML: ia memahami objek Python! Tidak memerlukan pengekod dan penyahkod tersuai. Berikut ialah siri/deserialisasi yang kompleks menggunakan YAML: 🎜 rrreee 🎜Seperti yang anda lihat, YAML mempunyai tatatanda sendiri untuk melabel objek Python. Outputnya masih sangat mudah dibaca. Objek datetime tidak memerlukan sebarang penanda khas kerana YAML sememangnya menyokong objek datetime. 🎜 🎜Prestasi🎜 🎜Sebelum anda mula memikirkan tentang prestasi, anda perlu mempertimbangkan sama ada prestasi adalah satu isu. Jika anda mensiri/menyahserialisasikan sejumlah kecil data dengan agak jarang (seperti membaca fail konfigurasi pada permulaan program anda), maka prestasi sebenarnya tidak menjadi masalah dan anda boleh meneruskan. 🎜 🎜Walau bagaimanapun, dengan mengandaikan anda memprofilkan sistem anda dan mendapati bahawa bersiri dan/atau penyahsirilan menyebabkan masalah prestasi, isu berikut perlu ditangani. 🎜 🎜Prestasi mempunyai dua aspek: berapa cepat penyiaran/penyahserialisasian, dan berapa besarkah perwakilan bersiri? 🎜 🎜Untuk menguji prestasi pelbagai format bersiri, saya akan mencipta struktur data yang lebih besar dan mensiri/menyahserikannya menggunakan Pickle, YAML dan JSON. Senarai big_data mengandungi 5,000 objek kompleks. 🎜 rrreee

Kimchi

🎜Saya akan menggunakan IPython di sini kerana ia mempunyai fungsi ajaib %timeit untuk mengukur masa pelaksanaan. 🎜 rrreee 🎜Acar lalai mengambil masa 83.1 milisaat untuk bersiri dan 29.2 milisaat untuk dinyahsiri, dengan saiz bersiri 747,328 bait. 🎜 🎜Mari cuba menggunakan protokol tertinggi. 🎜 rrreee 🎜Hasil yang menarik. Masa pensirilan menurun kepada hanya 21.2 ms, tetapi masa penyahsiran meningkat sedikit kepada 25.2 ms. Saiz bersiri dikurangkan dengan ketara kepada 394,350 bait (52%). 🎜

JSON

rrreee 🎜Baiklah. Prestasi pada pengekodan kelihatan lebih teruk sedikit daripada Pickle, tetapi prestasi pada penyahkodan jauh lebih teruk: 6x lebih perlahan. Apa yang berlaku? Ini ialah artifak fungsi object_hook yang perlu dijalankan untuk setiap kamus untuk menyemak sama ada ia perlu ditukar kepada objek. Ia berjalan lebih pantas tanpa menggunakan cangkuk objek. 🎜 rrreee 🎜Pelajaran di sini adalah untuk mempertimbangkan dengan teliti sebarang pengekodan tersuai semasa mensiri dan menyahsiri kepada JSON kerana ia boleh memberi kesan yang ketara pada prestasi keseluruhan. 🎜

YAML

rrreee 🎜Baiklah. YAML benar-benar perlahan. Walau bagaimanapun, perhatikan sesuatu yang menarik: saiz bersiri hanya 200,091 bait. Jauh lebih baik daripada Pickle dan JSON. Mari lihat sekilas ke dalam: 🎜 rrreee 🎜YAML sangat pandai di sini. Ia menentukan bahawa semua 5,000 kamus berkongsi nilai kunci "a" yang sama, jadi ia hanya menyimpannya sekali dan merujuknya menggunakan *id001 untuk semua objek. 🎜 🎜Keselamatan🎜 🎜Keselamatan selalunya menjadi isu kritikal. Pickle dan YAML terdedah kepada serangan pelaksanaan kod kerana pembinaan objek Python mereka. Fail yang diformat dengan bijak boleh mengandungi kod arbitrari yang akan dilaksanakan oleh Pickle atau YAML. Tidak perlu panik. Ini adalah dengan reka bentuk dan didokumenkan dalam dokumentasi Pickle: 🎜
Amaran: Modul jeruk tidak direka bentuk untuk melindungi daripada data yang salah atau dibina secara berniat jahat. Jangan sekali-kali membatalkan data yang diterima daripada sumber yang tidak dipercayai atau tidak disahkan.
🎜Dan apa yang terdapat dalam dokumen YAML: 🎜
Amaran: Adalah tidak selamat untuk memanggil yaml.load dengan sebarang data yang diterima daripada sumber yang tidak dipercayai! yaml.load adalah sekuat pickle.load, jadi ia boleh memanggil sebarang fungsi Python.
🎜Hanya tahu bahawa anda tidak sepatutnya menggunakan Pickle atau YAML untuk memuatkan data bersiri yang diterima daripada sumber yang tidak dipercayai. JSON baik-baik saja, tetapi jika anda mempunyai pengekod/penyahkod tersuai, anda mungkin terdedah juga. 🎜 🎜Modul yaml menyediakan fungsi yaml.safe_load() yang hanya memuatkan objek mudah, tetapi kemudian anda kehilangan banyak fungsi YAML dan mungkin memilih untuk menggunakan JSON sahaja. 🎜

Format lain

Terdapat banyak format bersiri lain yang tersedia. Berikut adalah sebahagian daripadanya.

Penimbal Protokol

Protobuf (iaitu Penampan Protokol) ialah format pertukaran data Google. Ia dilaksanakan dalam C++ tetapi mempunyai pengikatan Python. Ia mempunyai seni bina yang canggih dan membungkus data dengan cekap. Sangat berkuasa, tetapi tidak begitu mudah digunakan.

Pek Mesej

MessagePack ialah satu lagi format bersiri yang popular. Ia juga binari dan cekap, tetapi tidak seperti Protobuf ia tidak memerlukan skema. Ia mempunyai sistem jenis yang serupa dengan JSON, tetapi lebih kaya. Kekunci boleh terdiri daripada sebarang jenis, bukan hanya rentetan dan rentetan bukan UTF8 yang disokong.

CBOR

CBOR bermaksud Perwakilan Objek Binari Ringkas. Begitu juga, ia menyokong model data JSON. CBOR tidak setenar Protobuf atau MessagePack, tetapi ia menarik kerana dua sebab:

  1. Ia adalah standard Internet rasmi: RFC 7049.
  2. Ia direka untuk Internet Perkara (IoT).

Bagaimana untuk memilih?

Ini soalan besar. Begitu banyak pilihan, bagaimana anda memilih? Mari kita pertimbangkan pelbagai faktor yang perlu dipertimbangkan:

  1. Adakah format bersiri boleh dibaca oleh manusia dan/atau boleh diedit oleh manusia?
  2. Adakah anda akan menerima kandungan bersiri daripada sumber yang tidak dipercayai?
  3. Adakah penyiaran/penyahserialisasi merupakan hambatan prestasi?
  4. Adakah data bersiri perlu ditukar dengan persekitaran bukan Python?

Saya akan menjadikannya sangat mudah untuk anda dan melalui beberapa senario biasa dan format yang saya cadangkan untuk setiap satu:

Simpan keadaan tempatan program Python secara automatik

Gunakan jeruk (cPickle) dan HIGHEST_PROTOCOL di sini. Ia pantas, cekap dan boleh menyimpan serta memuatkan kebanyakan objek Python tanpa sebarang kod khas. Ia juga boleh digunakan sebagai cache berterusan tempatan.

Profil

Semestinya YAML. Tiada apa-apa yang mengalahkan kesederhanaannya untuk apa-apa yang manusia perlu baca atau edit. Ia telah berjaya digunakan oleh Ansible dan banyak projek lain. Dalam sesetengah kes, anda mungkin lebih suka menggunakan modul Python langsung sebagai fail konfigurasi. Ini mungkin pilihan yang tepat, tetapi ia bukan penyirian, ia sebenarnya sebahagian daripada program, bukan fail konfigurasi yang berasingan.

API Web

JSON adalah pemenang yang jelas di sini. Hari ini, API Web paling biasa digunakan oleh aplikasi web JavaScript yang menggunakan JSON secara asli. Sesetengah API web mungkin mengembalikan format lain (cth. csv untuk set hasil jadual padat), tetapi saya fikir anda boleh membungkus data csv ke dalam JSON dengan overhed minimum (tidak perlu mengulang setiap baris sebagai objek dengan semua nama lajur ).

Kapasiti tinggi/kependaman rendah komunikasi skala besar

Gunakan salah satu protokol binari: Protobuf (jika seni bina diperlukan), MessagePack atau CBOR. Jalankan ujian anda sendiri untuk mengesahkan prestasi dan keupayaan perwakilan setiap pilihan.

Kesimpulan

Siri dan penyahserikatan objek Python ialah aspek penting dalam sistem teragih. Anda tidak boleh menghantar objek Python terus melalui rangkaian. Anda selalunya perlu saling beroperasi dengan sistem lain yang dilaksanakan dalam bahasa lain, dan kadangkala anda hanya mahu menyimpan keadaan program anda dalam storan berterusan.

Python dilengkapi dengan beberapa skim bersiri dalam perpustakaan standardnya, dan banyak lagi tersedia sebagai modul pihak ketiga. Memahami semua pilihan dan kebaikan dan keburukan setiap satu akan membolehkan anda memilih kaedah yang paling sesuai dengan keadaan anda.

Atas ialah kandungan terperinci Pensirilan objek Python dan penyahserikatan: Bahagian 2. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn