2015-07-17 17 views
32

In base a questo comment e alla documentazione di riferimento, Pickle 4.0+ da Python 3.4+ dovrebbe essere in grado di decodificare gli oggetti byte superiori a 4 GB.Python 3 - Può decollare gestire oggetti byte più grandi di 4 GB?

Tuttavia, utilizzando Python 3.4.3 o pitone 3.5.0b2 su Mac OS X 10.10.4, ottengo un errore quando provo a salamoia un grande array di byte:

>>> import pickle 
>>> x = bytearray(8 * 1000 * 1000 * 1000) 
>>> fp = open("x.dat", "wb") 
>>> pickle.dump(x, fp, protocol = 4) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
OSError: [Errno 22] Invalid argument 

C'è un bug nel il mio codice o sto fraintendendo la documentazione?

+0

Non c'è nessun problema per me. Python 3.4.1 su Windows. – Jedi

+4

Interruzioni su OS X. Questo in realtà non ha nulla a che fare con pickle. 'open ('/ dev/null', 'wb'). write (bytearray (2 ** 31 - 1))' funziona, ma 'open ('/ dev/null', 'wb'). write (bytearray (2 ** 3)) 'genera quell'errore. Python 2 non ha questo problema. – Blender

+0

@Blender: ciò che genera un errore per te funziona con Python 2.7.10 e Python 3.4.3 (su OS X, versioni MacPorts). – EOL

risposta

12

Per riassumere ciò che è stato risposto nei commenti: gli oggetti più grandi di 4 GB

Sì, Python può decapare di byte. L'errore osservato è causato da un bug nell'implementazione (vedere Issue24658).

+5

In che modo questo problema non è ancora stato risolto? Insane – dpb

+1

È 2018 e il bug è ancora lì. Qualcuno sa perché? –

14

Ecco una soluzione semplice per issue 24658. Utilizzare pickle.loads o pickle.dumps e suddividere l'oggetto byte in blocchi di dimensioni 2**31 - 1 per farlo entrare o uscire dal file.

import pickle 
import os.path 

file_path = "pkl.pkl" 
n_bytes = 2**31 
max_bytes = 2**31 - 1 
data = bytearray(n_bytes) 

## write 
bytes_out = pickle.dumps(data) 
with open(file_path, 'wb') as f_out: 
    for idx in range(0, n_bytes, max_bytes): 
     f_out.write(bytes_out[idx:idx+max_bytes]) 

## read 
bytes_in = bytearray(0) 
input_size = os.path.getsize(file_path) 
with open(file_path, 'rb') as f_in: 
    for _ in range(0, input_size, max_bytes): 
     bytes_in += f_in.read(max_bytes) 
data2 = pickle.loads(bytes_in) 

assert(data == data2) 
+0

Grazie. Questo ha aiutato molto. Una cosa: per 'write' Qualora ' per IDX nella gamma (0, n_bytes, max_bytes): ' essere ' per IDX nella gamma (0, len (bytes_out), max_bytes): ' – naoko

2

lettura di un file da pezzi 2GB prende il doppio di memoria, come necessario se viene eseguita bytes concatenazione, il mio approccio alla carico sottaceti si basa su ByteArray:

class MacOSFile(object): 
    def __init__(self, f): 
     self.f = f 

    def __getattr__(self, item): 
     return getattr(self.f, item) 

    def read(self, n): 
     if n >= (1 << 31): 
      buffer = bytearray(n) 
      pos = 0 
      while pos < n: 
       size = min(n - pos, 1 << 31 - 1) 
       chunk = self.f.read(size) 
       buffer[pos:pos + size] = chunk 
       pos += size 
      return buffer 
     return self.f.read(n) 

Usage:

with open("/path", "rb") as fin: 
    obj = pickle.load(MacOSFile(fin)) 
+0

Sarà il codice di cui sopra lavoro per qualsiasi piattaforma? Se è così, il codice sopra è più simile a "FileThatAlsoCanBeLoadedByPickleOnOSX" giusto? Sto solo cercando di capire ... Non è come se usassi 'pickle.load (MacOSFile (fin))' su Linux questo si romperà, correggere? @markhor –

+0

Inoltre, implementeresti un metodo 'write'? –

1

Ecco la soluzione completa, anche se sembra pickle.load non tenta più di scaricare un file enorme più (I am su Python 3.5.2) in modo così rigoroso solo il pickle.dumps ha bisogno di questo per funzionare correttamente.

class MacOSFile(object): 

    def __init__(self, f): 
     self.f = f 

    def __getattr__(self, item): 
     return getattr(self.f, item) 

    def read(self, n): 
     # print("reading total_bytes=%s" % n, flush=True) 
     if n >= (1 << 31): 
      buffer = bytearray(n) 
      idx = 0 
      while idx < n: 
       batch_size = min(n - idx, 1 << 31 - 1) 
       # print("reading bytes [%s,%s)..." % (idx, idx + batch_size), end="", flush=True) 
       buffer[idx:idx + batch_size] = self.f.read(batch_size) 
       # print("done.", flush=True) 
       idx += batch_size 
      return buffer 
     return self.f.read(n) 

    def write(self, buffer): 
     n = len(buffer) 
     print("writing total_bytes=%s..." % n, flush=True) 
     idx = 0 
     while idx < n: 
      batch_size = min(n - idx, 1 << 31 - 1) 
      print("writing bytes [%s, %s)... " % (idx, idx + batch_size), end="", flush=True) 
      self.f.write(buffer[idx:idx + batch_size]) 
      print("done.", flush=True) 
      idx += batch_size 


def pickle_dump(obj, file_path): 
    with open(file_path, "wb") as f: 
     return pickle.dump(obj, MacOSFile(f), protocol=pickle.HIGHEST_PROTOCOL) 


def pickle_load(file_path): 
    with open(file_path, "rb") as f: 
     return pickle.load(MacOSFile(f)) 
0

Ho trovato anche questo problema, per risolvere questo problema, ho digitato il codice in diverse iterazioni. Diciamo che in questo caso ho 50.000 dati che devo calcolare tf-idf e fare knfic classfication. Quando corro e iterare direttamente 50.000 mi dà "quell'errore". Quindi, per risolvere questo problema lo blocco.

tokenized_documents = self.load_tokenized_preprocessing_documents() 
    idf = self.load_idf_41227() 
    doc_length = len(documents) 
    for iteration in range(0, 9): 
     tfidf_documents = [] 
     for index in range(iteration, 4000): 
      doc_tfidf = [] 
      for term in idf.keys(): 
       tf = self.term_frequency(term, tokenized_documents[index]) 
       doc_tfidf.append(tf * idf[term]) 
      doc = documents[index] 
      tfidf = [doc_tfidf, doc[0], doc[1]] 
      tfidf_documents.append(tfidf) 
      print("{} from {} document {}".format(index, doc_length, doc[0])) 

     self.save_tfidf_41227(tfidf_documents, iteration) 
Problemi correlati