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 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 yaml
pip 安装 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%)。
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 时,请仔细考虑任何自定义编码,因为它们可能会对整体性能产生重大影响。
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()
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
%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%). 🎜
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. 🎜
*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. 🎜Terdapat banyak format bersiri lain yang tersedia. Berikut adalah sebahagian daripadanya.
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.
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 bermaksud Perwakilan Objek Binari Ringkas. Begitu juga, ia menyokong model data JSON. CBOR tidak setenar Protobuf atau MessagePack, tetapi ia menarik kerana dua sebab:
Ini soalan besar. Begitu banyak pilihan, bagaimana anda memilih? Mari kita pertimbangkan pelbagai faktor yang perlu dipertimbangkan:
Saya akan menjadikannya sangat mudah untuk anda dan melalui beberapa senario biasa dan format yang saya cadangkan untuk setiap satu:
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.
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.
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 ).
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.
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!