복붙노트

[PYTHON] Python 3 - 4GB보다 큰 바이트 객체를 처리 할 수 ​​있습니까?

PYTHON

Python 3 - 4GB보다 큰 바이트 객체를 처리 할 수 ​​있습니까?

이 주석과 참조 된 문서를 토대로, Python 3.4+의 Pickle 4.0+는 4GB보다 큰 바이트 객체를 피클 할 수 있어야합니다.

그러나 Mac OS X 10.10.4에서 python 3.4.3 또는 python 3.5.0b2를 사용하면 큰 바이트 배열을 피클 할 때 오류가 발생합니다.

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

내 코드에 버그가 있습니까? 아니면 설명서를 오해하고 있습니까?

해결법

  1. ==============================

    1.다음은 문제 24658에 대한 간단한 해결책입니다. pickle.loads 또는 pickle.dumps를 사용하고 바이트 객체를 크기 2 ** 31 - 1 청크로 나누어 파일에서 가져 오거나 빠져 나오십시오.

    다음은 문제 24658에 대한 간단한 해결책입니다. pickle.loads 또는 pickle.dumps를 사용하고 바이트 객체를 크기 2 ** 31 - 1 청크로 나누어 파일에서 가져 오거나 빠져 나오십시오.

    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, len(bytes_out), 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)
    
  2. ==============================

    2.코멘트에서 답을 요약하면 다음과 같습니다.

    코멘트에서 답을 요약하면 다음과 같습니다.

    네, 파이썬은 4GB보다 큰 바이트 객체를 피클 할 수 있습니다. 관찰 된 오류는 구현상의 버그로 인해 발생합니다 (Issue24658 참조).

  3. ==============================

    3.pickle.load는 더 이상 거대한 파일을 더 이상 덤프하지 않으려 고합니다 (필자는 Python 3.5.2를 사용하고 있습니다). 그래서 엄밀히 말하면 pickle.dumps 만 제대로 작동해야합니다.

    pickle.load는 더 이상 거대한 파일을 더 이상 덤프하지 않으려 고합니다 (필자는 Python 3.5.2를 사용하고 있습니다). 그래서 엄밀히 말하면 pickle.dumps 만 제대로 작동해야합니다.

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

    4.바이트 연결을 수행하는 경우 2GB 청크로 파일을 읽는 데 필요한 메모리가 두 배가되고, pickle을로드하는 방식은 bytearray를 기반으로합니다.

    바이트 연결을 수행하는 경우 2GB 청크로 파일을 읽는 데 필요한 메모리가 두 배가되고, pickle을로드하는 방식은 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)
    

    용법:

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

    5.또한이 문제를 해결하기 위해 코드를 여러 번 반복했습니다. 말하자면이 경우에는 50.000 데이터를 계산해야하는데 tf-idf와 knf classfication을 수행해야합니다. 내가 50.000을 반복 실행하면 직접 "그 오류"가 발생합니다. 그래서,이 문제를 해결하기 위해 나는 그것을 덩어리로 만든다.

    또한이 문제를 해결하기 위해 코드를 여러 번 반복했습니다. 말하자면이 경우에는 50.000 데이터를 계산해야하는데 tf-idf와 knf classfication을 수행해야합니다. 내가 50.000을 반복 실행하면 직접 "그 오류"가 발생합니다. 그래서,이 문제를 해결하기 위해 나는 그것을 덩어리로 만든다.

    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)
    
  6. from https://stackoverflow.com/questions/31468117/python-3-can-pickle-handle-byte-objects-larger-than-4gb by cc-by-sa and MIT license