如何分块加载Pickle文件?

13

是否有选项可以分块加载pickle文件?

我知道我们可以将数据保存在CSV中并分块加载它。 但除了CSV之外,是否有选项可以分块加载pickle文件或任何Python原生文件?


2
你是在进行数据序列化,还是只是接收到了一个数据转储?如果你正在进行数据序列化,请举一个简短的例子说明你的数据以及如何进行序列化。 - kabanus
3
你好!无法分块读取pickle文件,但是你可以使用hdf格式进行操作。 - Grigory Skvortsov
3个回答

2
根据Python pickle的文档,目前不支持分块。
但是,可以将数据分成块,然后逐块读取。例如,假设原始结构为:
import pickle

filename = "myfile.pkl"
str_to_save = "myname"

with open(filename,'wb') as file_handle:
    pickle.dump(str_to_save, file_handle)
    
with open(filename,'rb') as file_handle:
    result = pickle.load(file_handle)

print(result)

这可以分成两个单独的pickle文件:

import pickle

filename_1 = "myfile_1.pkl"
filename_2 = "myfile_2.pkl"
str_to_save = "myname"

with open(filename_1,'wb') as file_handle:
    pickle.dump(str_to_save[0:4], file_handle)
with open(filename_2,'wb') as file_handle:
    pickle.dump(str_to_save[4:], file_handle)
    
with open(filename_1,'rb') as file_handle:
    result = pickle.load(file_handle)

print(result)

根据AKX的评论,将多个数据写入单个文件也可以正常工作:
import pickle

filename = "myfile.pkl"
str_to_save = "myname"

with open(filename,'wb') as file_handle:
    pickle.dump(str_to_save[0:4], file_handle)
    pickle.dump(str_to_save[4:], file_handle)
    
with open(filename,'rb') as file_handle:
    result = pickle.load(file_handle)
    print(result)
    result = pickle.load(file_handle)
    print(result)

3
Pickle对象可以连接成单个文件(并作为这样读取)。也就是说,您可以将多个对象使用pickle.dump()一次性写入同一个文件中。 - AKX
很酷,我没有想到那个。我添加了一个新的片段并引用了你的评论。 - Ben

1
据我了解,Pickle不支持按块进行load/dump操作。 Pickle本质上是通过“块”来读取完整的数据流,这些块的长度取决于数据流中的标志位。这就是序列化的全部内容。这个数据流本身可能在之前被切成了块(比如网络传输),但是块不能够实时地进行pickle/unpickle操作。

但是也许可以通过pickle“缓冲区”和“带外”功能实现非常大的数据的中间结果。

请注意,这并不完全是将单个pickle文件分段加载/保存。它仅适用于在序列化过程中遇到声明自己为“带外”(单独序列化)的对象。

引用Pickler class doc

如果buffer_callback不为None,则可以使用buffer view调用任意次数。如果回调返回false值(例如None),则给定的缓冲区为带外; 否则,缓冲区在带内序列化,即在pickle流中。(强调我的)

引用"Out of band" conceptdoc:

在某些情况下,pickle模块用于传输大量数据。因此,最小化内存复制的数量以保持性能和资源消耗可能非常重要。然而,pickle模块的正常操作将对象的图形结构转换为字节流的顺序流时,本质上涉及到从pickle流中复制数据。
如果提供程序(要传输的对象类型的实现)和使用者(通信系统的实现)都支持pickle协议5及更高版本提供的带外传输功能,则可以避免这种限制。
示例取自文档示例:
b = ZeroCopyByteArray(b"abc") # NB: class has a special __reduce_ex__ and _reconstruct method
buffers = []
data = pickle.dumps(b, protocol=5, buffer_callback=buffers.append)
# we could do things with these buffers like:
#  - writing each to a single file,
#  - sending them over network,
# ...
new_b = pickle.loads(data, buffers=buffers) # load in chunks

从这个例子中,我们可以考虑将每个缓冲区写入文件,或将每个缓冲区发送到网络。然后,反序列化将通过加载这些文件(或网络负载)并传递给拆包器来执行。
但请注意,在这个例子中我们最终得到了2个序列化数据:
- data - buffers
这并不是 OP 所期望的,也不完全是按块进行 pickle load/dump 的方式。
从 pickle-to-a-single-file 的角度来看,我认为这并没有任何好处,因为我们将不得不定义一种自定义方法来打包 data 和 buffers 到一个文件中,即定义一个新的数据格式...感觉就像破坏了 pickle 最初的优点。
引用 Unpickler构造函数文档

如果buffers不为None,则应该是可迭代的启用缓冲区的对象,每次pickle流引用带外缓冲区视图时都会消耗它们。这些缓冲区已按顺序提供给Pickler对象的buffer_callback。 自版本3.8起更改:添加了buffers参数。


0

我有一个类似的问题,在我编写一个文件描述符池时,注意到当我关闭文件描述符时,我的 pickle 文件会损坏。虽然您可以对打开的文件描述符执行多个 dump() 操作,但随后不可能执行 open('file', 'ab') 以开始保存新的对象集。

我通过在必须关闭文件描述符之前做一个pickler.dump(None)作为会话终止器来解决这个问题,并重新打开时,我实例化了一个新的Pickler实例来恢复向文件写入。

从此文件加载时,None对象表示会话结束,在此时我使用文件描述符实例化一个新的Pickler实例,以继续读取多会话 pickle 文件的其余部分。

但是,只有在某种原因下您必须关闭文件描述符时才适用于此。否则,任意数量的dump()调用都可以执行后面的load()


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接