Menyimpan Objek (Kegigihan Data)

233

Saya telah membuat objek seperti ini:

company1.name = 'banana' 
company1.value = 40

Saya ingin menyimpan objek ini. Bagaimana saya bisa melakukan itu?

Peterstone
sumber
1
Lihat contoh untuk orang yang datang ke sini untuk contoh sederhana cara menggunakan acar.
Martin Thoma
@ MartinThoma: Mengapa Anda (tampaknya) lebih suka jawaban itu dari yang diterima (dari pertanyaan terkait )?
martineau
Pada saat saya ditautkan, jawaban yang diterima tidak ada protocol=pickle.HIGHEST_PROTOCOL. Jawaban saya juga memberi alternatif untuk acar.
Martin Thoma

Jawaban:

449

Anda bisa menggunakan picklemodul di perpustakaan standar. Berikut ini adalah aplikasi dasar untuk contoh Anda:

import pickle

class Company(object):
    def __init__(self, name, value):
        self.name = name
        self.value = value

with open('company_data.pkl', 'wb') as output:
    company1 = Company('banana', 40)
    pickle.dump(company1, output, pickle.HIGHEST_PROTOCOL)

    company2 = Company('spam', 42)
    pickle.dump(company2, output, pickle.HIGHEST_PROTOCOL)

del company1
del company2

with open('company_data.pkl', 'rb') as input:
    company1 = pickle.load(input)
    print(company1.name)  # -> banana
    print(company1.value)  # -> 40

    company2 = pickle.load(input)
    print(company2.name) # -> spam
    print(company2.value)  # -> 42

Anda juga bisa mendefinisikan utilitas sederhana Anda sendiri seperti berikut ini yang membuka file dan menulis satu objek ke sana:

def save_object(obj, filename):
    with open(filename, 'wb') as output:  # Overwrites any existing file.
        pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)

# sample usage
save_object(company1, 'company1.pkl')

Memperbarui

Karena ini adalah jawaban yang sangat populer, saya ingin menyentuh beberapa topik penggunaan yang agak canggih.

cPickle(atau _pickle) vs.pickle

Hampir selalu lebih disukai untuk benar-benar menggunakan cPicklemodul daripada picklekarena yang pertama ditulis dalam C dan jauh lebih cepat. Ada beberapa perbedaan kecil di antara mereka, tetapi dalam kebanyakan situasi mereka setara dan versi C akan memberikan kinerja yang sangat unggul. Beralih ke itu tidak bisa lebih mudah, ubah saja importpernyataan ini:

import cPickle as pickle

Dalam Python 3, cPicklediganti namanya _pickle, tetapi melakukan ini tidak lagi diperlukan karena picklemodul sekarang melakukannya secara otomatis — lihat Apa perbedaan antara acar dan _pickle dalam python 3? .

Rundown-nya adalah Anda bisa menggunakan sesuatu seperti berikut untuk memastikan bahwa kode Anda akan selalu menggunakan versi C saat tersedia di Python 2 dan 3:

try:
    import cPickle as pickle
except ModuleNotFoundError:
    import pickle

Format aliran data (protokol)

pickledapat membaca dan menulis file dalam beberapa format Python khusus, berbeda, yang disebut protokol seperti yang dijelaskan dalam dokumentasi , "Protokol versi 0" adalah ASCII dan karenanya "dapat dibaca manusia". Versi> 0 adalah biner dan yang tertinggi tersedia tergantung pada versi Python apa yang digunakan. Standarnya juga tergantung pada versi Python. Dalam Python 2 standarnya adalah versi Protokol 0, tetapi dalam Python 3.8.1, itu adalah versi Protokol 4. Dalam Python 3.x modul telah pickle.DEFAULT_PROTOCOLditambahkan ke dalamnya, tetapi itu tidak ada di Python 2.

Untungnya ada singkatan untuk menulis pickle.HIGHEST_PROTOCOLdi setiap panggilan (dengan asumsi itu yang Anda inginkan, dan biasanya Anda lakukan), cukup gunakan angka literal -1- mirip dengan merujuk elemen terakhir dari urutan melalui indeks negatif. Jadi, alih-alih menulis:

pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)

Anda bisa menulis:

pickle.dump(obj, output, -1)

Bagaimanapun, Anda hanya perlu menentukan protokol sekali jika Anda membuat Picklerobjek untuk digunakan dalam beberapa operasi acar:

pickler = pickle.Pickler(output, -1)
pickler.dump(obj1)
pickler.dump(obj2)
   etc...

Catatan : Jika Anda berada di lingkungan yang menjalankan versi Python yang berbeda, maka Anda mungkin ingin menggunakan secara eksplisit (mis. Hardcode) nomor protokol khusus yang dapat dibaca oleh semuanya (versi selanjutnya umumnya dapat membaca file yang dihasilkan oleh yang sebelumnya) .

Banyak Objek

Sementara file acar dapat berisi sejumlah benda acar, seperti yang ditunjukkan dalam sampel di atas, ketika ada jumlah yang tidak diketahui dari mereka, itu sering lebih mudah untuk menyimpan mereka semua di semacam wadah bervariasi berukuran, seperti list, tuple, atau dictdan menulis semuanya ke file dalam satu panggilan:

tech_companies = [
    Company('Apple', 114.18), Company('Google', 908.60), Company('Microsoft', 69.18)
]
save_object(tech_companies, 'tech_companies.pkl')

dan pulihkan daftar dan semua yang ada di dalamnya nanti dengan:

with open('tech_companies.pkl', 'rb') as input:
    tech_companies = pickle.load(input)

Keuntungan utama adalah Anda tidak perlu tahu berapa banyak instance objek yang disimpan untuk memuatnya kembali nanti (walaupun melakukannya tanpa informasi yang mungkin, diperlukan beberapa kode yang sedikit khusus). Lihat jawaban untuk pertanyaan terkait Menyimpan dan memuat beberapa objek dalam file acar? untuk perincian tentang berbagai cara untuk melakukan ini. Secara pribadi saya suka jawaban @Lutz Prechelt yang terbaik. Ini dia disesuaikan dengan contoh di sini:

class Company:
    def __init__(self, name, value):
        self.name = name
        self.value = value

def pickled_items(filename):
    """ Unpickle a file of pickled data. """
    with open(filename, "rb") as f:
        while True:
            try:
                yield pickle.load(f)
            except EOFError:
                break

print('Companies in pickle file:')
for company in pickled_items('company_data.pkl'):
    print('  name: {}, value: {}'.format(company.name, company.value))
martineau
sumber
1
Ini langka bagi saya karena saya membayangkan akan ada cara yang lebih mudah untuk menyelamatkan suatu objek ... Sesuatu seperti 'saveobject (company1, c: \ mypythonobjects)
Peterstone
4
@Peterstone: Jika Anda hanya ingin menyimpan satu objek, Anda hanya perlu sekitar setengah dari kode pada contoh saya - Saya sengaja menulisnya seperti yang saya lakukan untuk menunjukkan bagaimana lebih dari satu objek dapat disimpan ke dalam (dan kemudian membaca kembali dari) file yang sama.
martineau
1
@Peterstone, ada alasan yang sangat bagus untuk pemisahan tanggung jawab. Dengan cara ini tidak ada batasan tentang bagaimana data dari proses pengawetan sedang digunakan. Anda dapat menyimpannya ke disk atau Anda juga dapat mengirimnya ke koneksi jaringan.
Harald Scheirich
3
@martinaeau, ini sebagai tanggapan terhadap komentar nada suara tentang seseorang yang seharusnya hanya memiliki satu fungsi untuk menyimpan objek ke disk. Tanggung jawab acar hanya untuk mengubah objek menjadi data yang dapat ditangani sebagai potongan. Menulis sesuatu ke file adalah tanggung jawab objek file. Dengan menjaga hal-hal yang terpisah satu memungkinkan penggunaan kembali yang lebih tinggi misalnya dapat mengirim data acar melintasi koneksi jaringan atau menyimpannya dalam database, semua tanggung jawab terpisah dari data aktual <-> konversi objek
Harald Scheirich
1
Anda menghapus company1dan company2. Mengapa Anda tidak juga menghapus Companydan menunjukkan apa yang terjadi?
Mike McKerns
49

Saya pikir itu asumsi yang cukup kuat untuk mengasumsikan bahwa objeknya adalah a class. Bagaimana jika itu bukan class? Ada juga anggapan bahwa objek tidak didefinisikan dalam interpreter. Bagaimana jika itu didefinisikan dalam interpreter? Juga, bagaimana jika atribut ditambahkan secara dinamis? Ketika beberapa objek python memiliki atribut yang ditambahkan ke __dict__setelah penciptaan mereka, pickletidak menghormati penambahan atribut tersebut (yaitu 'lupa' mereka ditambahkan - karena pickleserialisasi dengan mengacu pada definisi objek).

Dalam semua kasus ini, pickledan cPicklebisa mengecewakan Anda.

Jika Anda ingin menyimpan object(dibuat secara sewenang-wenang), di mana Anda memiliki atribut (baik ditambahkan dalam definisi objek, atau sesudahnya) ... taruhan terbaik Anda adalah menggunakan dill, yang dapat membuat cerita bersambung hampir semua hal dalam python.

Kami mulai dengan kelas ...

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pickle
>>> class Company:
...     pass
... 
>>> company1 = Company()
>>> company1.name = 'banana'
>>> company1.value = 40
>>> with open('company.pkl', 'wb') as f:
...     pickle.dump(company1, f, pickle.HIGHEST_PROTOCOL)
... 
>>> 

Sekarang tutup, dan mulai ulang ...

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pickle
>>> with open('company.pkl', 'rb') as f:
...     company1 = pickle.load(f)
... 
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1378, in load
    return Unpickler(file).load()
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 858, in load
dispatch[key](self)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1090, in load_global
    klass = self.find_class(module, name)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1126, in find_class
    klass = getattr(mod, name)
AttributeError: 'module' object has no attribute 'Company'
>>> 

Ups ... pickletidak bisa menanganinya. Ayo coba dill. Kami akan melempar tipe objek lain (a lambda) untuk ukuran yang baik.

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill       
>>> class Company:
...     pass
... 
>>> company1 = Company()
>>> company1.name = 'banana'
>>> company1.value = 40
>>> 
>>> company2 = lambda x:x
>>> company2.name = 'rhubarb'
>>> company2.value = 42
>>> 
>>> with open('company_dill.pkl', 'wb') as f:
...     dill.dump(company1, f)
...     dill.dump(company2, f)
... 
>>> 

Dan sekarang baca file.

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> with open('company_dill.pkl', 'rb') as f:
...     company1 = dill.load(f)
...     company2 = dill.load(f)
... 
>>> company1 
<__main__.Company instance at 0x107909128>
>>> company1.name
'banana'
>>> company1.value
40
>>> company2.name
'rhubarb'
>>> company2.value
42
>>>    

Berhasil. Alasannya picklegagal, dan dilltidak, adalah bahwa dillmemperlakukan __main__seperti modul (untuk sebagian besar), dan juga dapat acar definisi kelas daripada pengawetan dengan referensi (seperti picklehalnya). Alasannya dillbisa acar lambdaadalah karena memberi nama ... maka acar ajaib bisa terjadi.

Sebenarnya, ada cara yang lebih mudah untuk menyimpan semua objek ini, terutama jika Anda memiliki banyak objek yang Anda buat. Cukup buang seluruh sesi python, dan kembali lagi nanti.

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> class Company:
...     pass
... 
>>> company1 = Company()
>>> company1.name = 'banana'
>>> company1.value = 40
>>> 
>>> company2 = lambda x:x
>>> company2.name = 'rhubarb'
>>> company2.value = 42
>>> 
>>> dill.dump_session('dill.pkl')
>>> 

Sekarang matikan komputer Anda, nikmati espresso atau apa pun, dan kembali lagi nanti ...

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> dill.load_session('dill.pkl')
>>> company1.name
'banana'
>>> company1.value
40
>>> company2.name
'rhubarb'
>>> company2.value
42
>>> company2
<function <lambda> at 0x1065f2938>

Satu-satunya kelemahan utama adalah bahwa dillitu bukan bagian dari perpustakaan standar python. Jadi jika Anda tidak dapat menginstal paket python di server Anda, maka Anda tidak dapat menggunakannya.

Namun, jika Anda dapat menginstal paket python di sistem Anda, Anda bisa mendapatkan yang terbaru dilldengan git+https://github.com/uqfoundation/dill.git@master#egg=dill. Dan Anda bisa mendapatkan versi rilis terbaru dengan pip install dill.

Mike McKerns
sumber
Saya mendapatkan TypeError: __new__() takes at least 2 arguments (1 given)ketika mencoba menggunakan dill(yang terlihat menjanjikan) dengan objek yang agak rumit yang mencakup file audio.
MikeiLL
1
@ MikeikeL: Anda mendapatkan TypeErrorketika Anda melakukan apa, tepatnya? Itu biasanya tanda memiliki jumlah argumen yang salah ketika membuat instance kelas. Jika ini bukan bagian dari alur kerja dari pertanyaan di atas, dapatkah Anda mempostingnya sebagai pertanyaan lain, mengirimkannya kepada saya melalui email, atau menambahkannya sebagai masalah di dillhalaman github?
Mike McKerns
3
Bagi siapa pun yang mengikuti, berikut pertanyaan terkait @MikeLL yang diposkan - dari jawabannya, tampaknya bukan dillmasalah.
martineau
dilTapi aku memberiku MemoryError! begitu juga cPickle, pickledan hickle.
Färid Alijani
4

Anda dapat menggunakan cache apa pun untuk melakukan pekerjaan untuk Anda. Itu mempertimbangkan semua detail:

  • Menggunakan dill sebagai backend, yang memperluas picklemodul python untuk menangani lambdadan semua fitur python yang bagus.
  • Ini menyimpan objek yang berbeda ke file yang berbeda dan memuatnya dengan benar.
  • Membatasi ukuran cache
  • Mengizinkan pembersihan cache
  • Mengizinkan berbagi objek di antara beberapa proses
  • Mengizinkan penghormatan terhadap file input yang memengaruhi hasil

Dengan asumsi Anda memiliki fungsi myfuncyang menciptakan instance:

from anycache import anycache

class Company(object):
    def __init__(self, name, value):
        self.name = name
        self.value = value

@anycache(cachedir='/path/to/your/cache')    
def myfunc(name, value)
    return Company(name, value)

Anycache memanggil myfuncpada saat pertama dan mengambil hasilnya ke file dalam cachedirmenggunakan pengidentifikasi unik (tergantung pada nama fungsi dan argumennya) sebagai nama file. Pada proses yang berurutan, objek yang diambil akan dimuat. Jika cachedirdiawetkan antara menjalankan python, objek acar diambil dari jalankan python sebelumnya.

Untuk detail lebih lanjut lihat dokumentasi

c0fec0de
sumber
Bagaimana seseorang menggunakan anycacheuntuk menyimpan lebih dari satu contoh dari, katakanlah, classatau wadah seperti list(itu bukan hasil dari memanggil fungsi)?
martineau
2

Contoh cepat menggunakan company1dari pertanyaan Anda, dengan python3.

import pickle

# Save the file
pickle.dump(company1, file = open("company1.pickle", "wb"))

# Reload the file
company1_reloaded = pickle.load(open("company1.pickle", "rb"))

Namun, seperti yang dicatat oleh jawaban ini , acar sering gagal. Jadi Anda harus benar-benar menggunakannya dill.

import dill

# Save the file
dill.dump(company1, file = open("company1.pickle", "wb"))

# Reload the file
company1_reloaded = dill.load(open("company1.pickle", "rb"))
Anthony Ebert
sumber