Membongkar objek python 2 dengan python 3

129

Saya bertanya-tanya apakah ada cara untuk memuat objek yang diasamkan di Python 2.4, dengan Python 3.4.

Saya telah menjalankan 2to3 pada sejumlah besar kode warisan perusahaan untuk mendapatkan yang terbaru.

Setelah melakukan ini, saat menjalankan file saya mendapatkan kesalahan berikut:

  File "H:\fixers - 3.4\addressfixer - 3.4\trunk\lib\address\address_generic.py"
, line 382, in read_ref_files
    d = pickle.load(open(mshelffile, 'rb'))
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 1: ordinal
not in range(128)

Melihat objek acar dalam pertikaian, itu adalah dictdalam dict, berisi kunci dan nilai-nilai jenis str.

Jadi pertanyaan saya adalah: Apakah ada cara untuk memuat objek, awalnya asinan dengan python 2.4, dengan python 3.4?

NDevox
sumber
1
Apakah Python 2.4 memiliki jsonmodul? Mungkin Anda bisa menulis skrip 2.4 yang menghapus objek dan menyimpannya sebagai objek json, dan kemudian menulis skrip 3.4 yang membaca objek json dan menyimpannya sebagai objek acar yang kompatibel dengan 3.4. Ini akan menjadi operasi satu kali yang Anda jalankan pada semua file acar Anda.
Kevin
Saya memikirkan hal yang sama, mengingat ini adalah dikt, saya rasa saya hanya bisa mengubah sys.stdout ke file dan mencetaknya, tetapi saya ingin melihat apakah saya dapat memuatnya terlebih dahulu
NDevox
Pertanyaan terkait yang berkaitan dengan datetimes khusus: stackoverflow.com/questions/24805105/…
John Y

Jawaban:

189

Anda harus memberi tahu pickle.load()cara mengonversi data bytestring Python ke string Python 3, atau Anda dapat memberi tahu pickleuntuk membiarkannya sebagai byte.

Standarnya adalah mencoba dan mendekode semua data string sebagai ASCII, dan gagal mendekode itu. Lihat pickle.load()dokumentasi :

Argumen kata kunci opsional adalah fix_import , pengkodean dan kesalahan , yang digunakan untuk mengontrol dukungan kompatibilitas untuk aliran acar yang dihasilkan oleh Python 2. Jika fix_import benar, acar akan mencoba memetakan nama-nama Python 2 lama ke nama-nama baru yang digunakan dalam Python 3. penyandian dan kesalahan memberitahu acar bagaimana cara men-decode instance string 8-bit yang diambil oleh Python 2; default ini untuk 'ASCII' dan 'ketat', masing-masing. The encoding bisa 'byte' untuk membaca kasus 8-bit string ini sebagai byte obyek.

Mengatur pengodean ke latin1memungkinkan Anda untuk mengimpor data secara langsung:

with open(mshelffile, 'rb') as f:
    d = pickle.load(f, encoding='latin1') 

tetapi Anda harus memverifikasi bahwa tidak ada string Anda yang diterjemahkan menggunakan codec yang salah; Latin-1 berfungsi untuk input apa pun karena memetakan nilai byte 0-255 ke 256 titik kode Unicode pertama secara langsung.

Alternatifnya adalah memuat data dengan encoding='bytes', dan mendekode semua byteskunci dan nilai sesudahnya.

Perhatikan bahwa hingga versi Python sebelum 3.6.8, 3.7.2 dan 3.8.0, pembongkaran datetimedata objek Python 2 rusak kecuali jika Anda menggunakan encoding='bytes'.

Martijn Pieters
sumber
1
Bagaimana ini bisa dibuat kompatibel dengan Python 2? Tampaknya, argumen penyandian tidak ada untuk Python 2.
EpicAdv
2
@ EpicAdv: Anda tidak perlu membuat kode ini kompatibel dengan Python 2; pertanyaan ini adalah tentang bagaimana memuat acar Python 2 ke dalam Python 3. Jatuhkan encodingkata kunci sama sekali untuk Python 2.
Martijn Pieters
10
@ EpicAdv: Anda dapat membuat kamus pickle_options yang kosong untuk python 2 atau memiliki 'encoding': 'latin1'dan mengirim ** pickle_options ke acar. Dengan cara ini harus dijalankan di kedua versi.
pipefish
@pipefish - Pintar, tetapi di suatu tempat Anda harus mendeteksi versi mana yang Anda gunakan, sehingga Anda juga bisa lebih mudah melakukan panggilan secara berbeda (satu dengan dan satu tanpa argumen tambahan) tergantung pada versi. Tetapi setidaknya Anda mendapatkan intisari dari komentar EpicAdv, yang mana komentar Martijn tidak membahas sama sekali.
John Y
2
Saya menyadari bahwa datetimekomentar itu bukanlah dorongan utama dari jawaban ini, tetapi bagi pembaca di masa mendatang, saya ingin menunjukkan bahwa bahkan versi "tetap" dari Python 3 masih perlu encoding='latin-1'membongkar datetime Python 2. Jika data acar Python 2 Anda kebetulan menyertakan datetimes dan bytestrings yang dikodekan dalam sesuatu selain Latin-1, maka Anda mungkin masih lebih baik menggunakan encoding='bytes'setelah semua.
John Y
15

Menggunakan encoding='latin1'menyebabkan beberapa masalah saat objek Anda berisi array numpy di dalamnya.

Menggunakan encoding='bytes'akan lebih baik.

Silakan lihat jawaban ini untuk penjelasan lengkap tentang penggunaanencoding='bytes'

Sreeragh AR
sumber
Masalah yang mana? Apa yang harus saya perhatikan? menggunakan bytesmake strings menjadi byte (), jadi saya lebih suka latin1jika mungkin, tetapi tidak jelas bagi saya apa masalahnya.
Gulzar
2
@ sreeragh-ar: Bisakah Anda memberi contoh masalah yang Anda temui? Saya memiliki numpy.ndarrayacar dua dimensi (numpy 1,14) menggunakan Python 2.7 cPickle.dumps(), dan membongkar Python 3 dengan pickle.loads(..., encoding='latin1')berfungsi dengan baik.
djvg
@djvg Saya menghadapi masalah ketika saya harus mengambil gambar sebagai string gambar dan menghapusnya. Kode dapat ditemukan di sini. gist.github.com/sreeragh-ar/70205db3a43badbfa69f758faa898be3
Sreeragh AR
@Gulzar Silakan lihat inti di atas untuk masalah ini. Gambar menjadi rusak setelah dibongkar.
Sreeragh AR