Bir Nesneyi Kaydetme (Veri kalıcılığı)


233

Ben böyle bir nesne yarattım:

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

Bu nesneyi kaydetmek istiyorum. Bunu nasıl yapabilirim?


1
Turşu nasıl kullanacağınıza dair basit bir örnek için buraya gelen insanlar için örneğe bakın .
Martin Thoma

@ MartinThoma: Neden (görünüşte) bu cevabı ( bağlantılı sorunun ) kabul edilen cevabına tercih ediyorsunuz ?
martineau

Bağlantı kurduğum sırada, kabul edilen cevap yoktu protocol=pickle.HIGHEST_PROTOCOL. Cevabım turşu için de alternatifler sunuyor.
Martin Thoma

Yanıtlar:


449

Sen kullanabilirsiniz picklestandart kütüphanede modülü. İşte örneğinize temel bir uygulama:

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

Ayrıca, bir dosyayı açan ve ona tek bir nesne yazan aşağıdaki gibi kendi basit yardımcı programınızı tanımlayabilirsiniz:

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')

Güncelleme

Bu çok popüler bir cevap olduğu için, biraz gelişmiş kullanım konularına değinmek istiyorum.

cPickle(veya _pickle) vspickle

Birincisi C ile yazılmış ve çok daha hızlı olduğu için cPicklemodülü kullanmak her zaman tercih edilir pickle. Aralarında bazı ince farklılıklar vardır, ancak çoğu durumda eşdeğerdirler ve C versiyonu büyük ölçüde üstün performans sağlar. Buna geçmek daha kolay olamazdı, importifadeyi şu şekilde değiştirin :

import cPickle as pickle

Python 3'te cPickleyeniden adlandırıldı _pickle, ancak picklemodül artık otomatik olarak yaptığı için bunu yapmak artık gerekli değil - python 3'te turşu ve _pickle arasındaki fark nedir? .

Yıkık, kodunuzun hem Python 2 hem de 3'te mevcut olduğunda her zaman C sürümünü kullanmasını sağlamak için aşağıdaki gibi bir şey kullanabilmenizdir:

try:
    import cPickle as pickle
except ModuleNotFoundError:
    import pickle

Veri akışı biçimleri (protokoller)

pickleokumak ve birkaç farklı yazma dosyaları, Python özgü, biçimleri denilen protokolleri anlatıldığı gibi belgeler , "Protokolü sürüm 0" ASCII ve bu nedenle "insan tarafından okunabilir" dir. > 0 sürümleri ikiliktir ve kullanılabilir en yüksek sürüm Python'un hangi sürümünün kullanıldığına bağlıdır. Varsayılan değer Python sürümüne de bağlıdır. Python 2'de varsayılan Protokol sürümüdür 0, ancak Python 3.8.1'de Protokol sürümüdür 4. Python 3.x'de modülün bir eki vardı pickle.DEFAULT_PROTOCOL, ancak bu Python 2'de mevcut değil.

Neyse ki pickle.HIGHEST_PROTOCOL, her çağrıda yazmak için kısayol vardır (istediğiniz şey olduğunu ve genellikle yaptığınızı varsayarak), -1bir dizinin son öğesine negatif bir dizin yoluyla başvurmaya benzer şekilde sadece değişmez sayıyı kullanın . Yani, yazmak yerine:

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

Sadece şunu yazabilirsiniz:

pickle.dump(obj, output, -1)

Her iki durumda da, Picklerçoklu turşu işlemlerinde kullanmak için bir nesne oluşturduysanız protokolü yalnızca bir kez belirtmeniz gerekir :

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

Not : Python'un farklı sürümlerini çalıştıran bir ortamdaysanız, muhtemelen hepsinin okuyabileceği belirli bir protokol numarasını açıkça kullanmak (yani sabit kod) kullanmak istersiniz (sonraki sürümler genellikle önceki sürümler tarafından üretilen dosyaları okuyabilir) .

Birden Çok Nesne

Turşu dosyası iken olabilir bunlardan bilinmeyen numara varken, yukarıdaki örneklerde görüldüğü gibi salamura nesnelerin herhangi sayısına sahiptirler, bu değişken boyutlu konteynerin çeşit saklayabilir bir benzeri genellikle daha kolaydır list, tupleya dictve yazma hepsini tek bir çağrıda dosyaya aktarın:

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

ve listeyi ve içindeki her şeyi daha sonra geri yükleyin:

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

Önemli bir avantajı (yani bilgi olmadan bunu yaparken her ne kadar daha sonra onları geri yüklemek için kaydedilmiş kaç nesne örnekleri bilmek gerekmez olduğunu ise mümkün, bazı hafif uzman kod gerektirir). İlgili sorunun yanıtlarına bakın Turşu dosyasında birden çok nesne kaydetme ve yükleme? bunu yapmanın farklı yollarıyla ilgili ayrıntılar için. Şahsen ben @Lutz Prechelt'in en iyi cevabını seviyorum . İşte buradaki örneklere uyarlanmıştır:

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))

1
Bu benim için nadir, çünkü bir nesneyi kaydetmenin daha kolay bir yolu olacağını hayal ettim ... 'saveobject (company1, c: \ mypythonobjects) gibi bir şey
Peterstone

4
@Peterstone: Yalnızca bir nesneyi saklamak istiyorsanız, örneğimdeki kodun yaklaşık yarısına ihtiyacınız olacaktır - bunu, birden fazla nesnenin nasıl kaydedilebileceğini göstermek için yaptığım şekilde yazdım (ve daha sonra tekrar okuyun) aynı dosyadan).
martineau

1
@Peterstone, sorumlulukların ayrılması için çok iyi bir neden var. Bu şekilde, dekapaj işleminden elde edilen verilerin nasıl kullanıldığı konusunda bir sınırlama yoktur. Diske kaydedebilir veya bir ağ bağlantısı üzerinden de gönderebilirsiniz.
Harald Scheirich

3
@martinaeau, bu perstones yanıt bir yanıt bir nesneyi diske kaydetmek için sadece bir işlevi olması gerektiği idi. Turşuların sorumluluğu sadece bir nesneyi yığın olarak işlenebilecek verilere dönüştürmektir. Bir şeyleri dosyaya yazmak dosya nesnelerinin sorumluluğudur. İşleri ayrı tutmak, örneğin bir ağ bağlantısı üzerinden turşu veri gönderebilmek veya bir veritabanında saklamak gibi daha yüksek yeniden kullanıma olanak tanır, tüm sorumluluklar gerçek verilerden ayrıdır <-> nesne dönüşümü
Harald Scheirich

1
Silmek company1ve company2. Neden Companyne olduğunu silmiyor ve göstermiyorsun?
Mike McKerns

49

Bence nesnenin bir olduğunu varsaymak oldukça güçlü bir varsayım class. Ya bir değilse class? Ayrıca nesnenin yorumlayıcıda tanımlanmadığı varsayımı vardır. Tercümanda tanımlanmışsa ne olur? Ayrıca, öznitelikler dinamik olarak eklenirse ne olur? Bazı python nesneleri __dict__oluşturulduktan sonra eklenmiş özniteliklere sahip olduğunda pickle, bu özniteliklerin eklenmesine saygı göstermez (yani, eklendikleri 'unutur' - çünkü picklenesne tanımına referansla serileştirir).

Tüm bu durumlarda pickleve cPicklekorkunç bir şekilde başarısız olabilir.

object(Keyfi olarak oluşturulmuş) bir özellik kaydetmek istiyorsanız (nesne tanımına eklenmiş veya daha sonra)… dillpython'da neredeyse her şeyi serileştirebilecek en iyi bahsiniz kullanmaktır .

Bir sınıfla başlıyoruz…

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)
... 
>>> 

Şimdi kapat ve yeniden başlat ...

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'
>>> 

Hata ... picklebaşa çıkamıyor. Deneyelim dill. lambdaİyi bir ölçü için başka bir nesne türü (a ) atacağız .

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)
... 
>>> 

Ve şimdi dosyayı okuyun.

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
>>>    

İşe yarıyor. Nedeni picklebaşarısız olur ve dillyapmaz, bir modül gibi dilldavranır __main__(çoğunlukla) ve aynı zamanda referansla dekapaj yapmak yerine sınıf tanımlarını da seçebilir (olduğu gibi pickle). A dillturşusunun sebebi , lambdaona bir isim vermesidir… daha sonra dekapaj büyüsü olabilir.

Aslında, özellikle oluşturduğunuz çok sayıda nesneniz varsa, tüm bu nesneleri kaydetmenin daha kolay bir yolu vardır. Sadece tüm python oturumunu boşaltın ve daha sonra tekrar gelin.

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')
>>> 

Şimdi bilgisayarınızı kapatın, bir espresso veya başka bir şeyin tadını çıkarın ve daha sonra tekrar gelin ...

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>

Tek büyük dezavantajı, dillpython standart kütüphanesinin bir parçası olmamasıdır. Eğer sunucunuza bir python paketi yükleyemezseniz, onu kullanamazsınız.

Sisteminizde piton paketlerini yüklemek edebiliyoruz Ancak, son alabilirsiniz dillile git+https://github.com/uqfoundation/dill.git@master#egg=dill. Ve ile en son yayınlanan sürümü alabilirsiniz pip install dill.


Bir ses dosyası içeren oldukça karmaşık bir nesne ile (umut verici görünüyor) TypeError: __new__() takes at least 2 arguments (1 given)kullanmaya çalışırken bir alıyorum dill.
MikeiLL

1
@MikeiLL: TypeErrorTam olarak ne yaptığını anlıyor musun? Bu genellikle bir sınıf örneğini başlatırken yanlış sayıda argümana sahip olmanın bir işaretidir. Bu, yukarıdaki sorunun iş akışının bir parçası değilse, başka bir soru olarak gönderebilir, bana e-posta ile gönderebilir veya dillgithub sayfasına bir sorun olarak ekleyebilir misiniz?
Mike McKerns

3
Takip eden herkes için, @MikeLL'in gönderdiği ilgili soru - cevaptan, görünüşe göre bir dillsorun değildi .
martineau

dilMemoryErrorYine de bana veriyorum! yapar cPickle, pickleve hickle.
Färid Alijani

4

İşi sizin için yapmak için anycache kullanabilirsiniz . Tüm ayrıntıları dikkate alır:

  • Arka uç olarak dereotu kullanır , bu da python picklemodülünü işlemek lambdave tüm güzel python özelliklerini genişletir .
  • Farklı nesneleri farklı dosyalara depolar ve düzgün bir şekilde yeniden yükler.
  • Önbellek boyutunu sınırlar
  • Önbellek temizlemeye izin verir
  • Nesnelerin birden fazla çalışma arasında paylaşılmasına izin verir
  • Sonucu etkileyen girdi dosyalarına saygı gösterilmesini sağlar

Örneği myfuncoluşturan bir fonksiyonunuz olduğunu varsayarsak :

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 myfuncilk kez çağırır ve dosya cachediradı olarak benzersiz bir tanımlayıcı (işlev adına ve bağımsız değişkenlerine bağlı olarak ) kullanarak sonucu bir dosyaya alır . Ardışık herhangi bir çalışmada, turşu nesnesi yüklenir. Eğer cachedirpiton koşular arasında korunur, turşu nesne önceki piton vadede alınır.

Daha fazla ayrıntı için belgelere bakın


anycacheÖrneğin, a classveya kapsayıcı gibi birden fazla örneğini kaydetmek için nasıl kullanılır list(bir işlev çağrılmasının sonucu değildi)?
martineau

2

company1Sorunuzdan python3 ile hızlı örnek .

import pickle

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

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

Ancak, bu cevabın belirttiği gibi, turşu sık sık başarısız olur. Yani gerçekten kullanmalısın 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"))
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.