【问题标题】:Why doesn't appending binary pickles work?为什么附加二进制泡菜不起作用?
【发布时间】:2010-05-18 14:05:04
【问题描述】:

我知道这并不完全是 pickle 模块的用途,但我原以为这会起作用。我正在使用 Python 3.1.2

这是后台代码:

import pickle

FILEPATH='/tmp/tempfile'

class HistoryFile():
    """
    Persistent store of a history file  
    Each line should be a separate Python object
    Usually, pickle is used to make a file for each object,
        but here, I'm trying to use the append mode of writing a file to store a sequence
    """

    def validate(self, obj):
        """
        Returns whether or not obj is the right Pythonic object
        """
        return True

    def add(self, obj):
        if self.validate(obj):
            with open(FILEPATH, mode='ba') as f:    # appending, not writing
                f.write(pickle.dumps(obj))
        else:
            raise "Did not validate"

    def unpack(self):
        """
        Go through each line in the file and put each python object
        into a list, which is returned
        """
        lst = []
        with open(FILEPATH, mode='br') as f:
            # problem must be here, does it not step through the file?
            for l in f:
                lst.append(pickle.loads(l))
        return lst

现在,当我运行它时,它只打印出传递给类的第一个对象。

if __name__ == '__main__':

    L = HistoryFile()
    L.add('a')
    L.add('dfsdfs')
    L.add(['dfdkfjdf', 'errree', 'cvcvcxvx'])

    print(L.unpack())       # only prints the first item, 'a'!

这是因为它看到了早期的 EOF 吗?也许附加仅适用于ascii? (在这种情况下,为什么让我做 mode='ba'?)有没有更简单的方法来做到这一点?

【问题讨论】:

  • 我不知道pickle模块,我的python I/O有点生锈,但我猜你可能正在从文件中读取行,而对象存储时没有换行符分隔(实际上,其中可能包含换行符,因此如果是这种情况,您可能需要更改它)

标签: python pickle


【解决方案1】:

为什么你认为附加二进制泡菜会产生一个泡菜?! Pickling 可以让您一个接一个地放置(并取回)多个项目,因此显然它必须是一种“自终止”序列化格式。忘记台词,把它们找回来!例如:

>>> import pickle
>>> import cStringIO
>>> s = cStringIO.StringIO()
>>> pickle.dump(23, s)
>>> pickle.dump(45, s)
>>> s.seek(0)
>>> pickle.load(s)
23
>>> pickle.load(s)
45
>>> pickle.load(s)
Traceback (most recent call last):
   ...
EOFError
>>> 

当你完成解封时,只需抓住EOFError 告诉你。

【讨论】:

  • 是的,但是类必须打开文件以获得写入权限,这会擦除文件。我想保留它。这就是我想附加文件的原因。那么在打开写作之前阅读内容的标准做法是什么?
  • @Adam,只需使用'r+' 打开(或者更好的'r+b',这样您就可以使用pickle.HIGHEST_PROTOCOL 的协议进行选择!),参见docs.python.org/library/functions.html?highlight=open#open 和docs.python.org/library/…。
  • @Alex 明白了;谢谢。我对 open 例程中的 + 没有给予足够的关注。真的很简单。仍然不确定 HIGHEST_PROTOCOL 的用途是什么,在 python3 中,他们推荐协议 3,这无论如何都是默认的。 ...
  • @Adam,HIGHEST_PROTOCOL,又名-1,意思是“你能做的最好的”,除非你需要你的泡菜可以被旧版本的 Python 加载,否则你应该一直使用它。在 Python 2.* 中,默认值为 0(ascii 协议),因此它必须保持向后兼容性——因为它必须在 Python 3.* 中永远保持 3,即使明天有人发明了一种新格式,它需要一半时间和空间(不太可能,我知道;-)。因此,如果您不关心制作旧 Python 版本可读的泡菜,请始终使用 -1!-)
  • 刚试了一下,mode='r+b' 好像不行。查看文档,现在理解得更好,我尝试了“a+b”,它按预期工作。我知道泡菜不是这样设计的,但事实证明你可以在文件中写入一系列泡菜!
【解决方案2】:

答案是它确实可以工作,但是如果没有“+”模式,open 的附加功能自动添加的换行符会将二进制文件与字符串数据混合在一起(绝对禁止)。更改此行:

with open(FILEPATH, mode='ab') as f:    # appending, not writing
    f.write(pickle.dumps(obj))

到

with open(FILEPATH, mode='a+b') as f:    # appending, not writing
    pickle.dump(obj, f)

Alex 还指出,为了获得更大的灵活性,请使用 mode='r+b',但这需要适当的搜索。由于我想创建一个行为类似于先进后出的 Python 对象序列的历史文件,因此尝试在文件中附加对象实际上是有意义的。我只是做得不对:)

不需要单步执行文件,因为(呃!)它是序列化的。所以替换:

for l in f:
    lst.append(pickle.loads(l))

与

while 1:
    try:
        lst.append(pickle.load(f))
    except IOError:
        break

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-02-05
    • 1970-01-01
    • 1970-01-01
    • 2020-12-07
    • 1970-01-01
    • 1970-01-01
    • 2014-03-12
    相关资源
    最近更新 更多