Python:如何将带有对象的列表保存到文件中?

4

我正在尝试创建不同的对象(使用类和对象)并将它们保存在文件中以便以后编辑或检索。但是它看起来像这样。

GlobalCategories=[]
GlobalContent=[]
def LoadData(x,y):
   import pickle
   with open('bin.dat') as f:
       x,y = pickle.load(f)


def SaveData(x,y):
   import pickle
   with open('bin.dat', 'wb') as f:
      pickle.dump([x,y], f)

def Loader(x,y):
     try:
          LoadData(x,y)
     except:
          SaveData(x,y)

以下是示例代码,展示了我如何保存列表信息(其中"tema"是类名,其它部分为该类的方法):

newtheme=Tema()
newtheme.setInfo_name(newstr)
newtheme.setInfo_code(newcode)
GlobalCategories.append(newtheme)
SaveData(GlobalContent,GlobalCategories)

X和Y是全局列表,我在其中存储对象(我注意到它将每个对象的方向保存在内存中)。当我第一次运行它时,它创建文件并将信息保存在文件中,但是如果我关闭它,再次运行它并加载信息,则程序会删除信息并重新创建文件,因此存储的所有内容都消失了。

我不知道这是否是存储对象的正确方法,或者是否有更好的方法,因此非常欢迎任何建议。

@abernert:谢谢您,abarnert! 我想做的是保存一个包含两个列表的列表。例如,一个列表将保存制造商(丰田,尼桑等),另一个列表将保存汽车型号(Tundra,Murano)。现在,每个元素都是创建时添加到列表中的对象。

newtheme=Theme() newtheme.setInfo_name(newstr) GlobalCategories.append(newtheme)

这就是我如何在全局列表中保存对象。GlobalCategories是我想要在关闭程序后稍后加载的那两个列表中的一个(它将类似于上述示例中的汽车公司列表)。现在,我遇到的问题是,在关闭并重新启动程序后从列表中加载对象,因为当我没有关闭shell时,我能够检索和编辑它们。

我需要在启动程序后将制造商和汽车对象分别存储在各自的列表中,以便稍后对它们进行操作。再次感谢abernert!


2
你能展示一下调用这些函数的代码吗? - Neil
你的 LoadData 函数并没有做任何有用的事情,它只是重新绑定了本地名称 xy。在 Python 中,你不能像那样通过引用传递名称。 - abarnert
同时,使用不记录任何日志的裸 except 子句将使调试代码变得非常非常困难。将 Loader 更改为 except Exception as e: 并添加 print(e) 或其他内容,以查看它是否意外地未能加载数据,而是在您不想要它的时候进行了保存数据。 - abarnert
2个回答

8

如果没有上下文说明你如何使用LoadDataSaveData函数,很难知道问题所在。不过,这里有一个小演示来展示我认为你想要的操作。

import pickle
import random

def load_data():
    try:
        with open("bin.dat") as f:
            x, y = pickle.load(f)
    except:
        x, y = [], []
    return x, y

def save_data(data):
    with open("bin.dat", "wb") as f:
        pickle.dump(data, f)

if __name__ == "__main__":
    x, y = load_data()
    print x, y
    x.append(random.randint(1, 10))
    y.append(random.randint(1, 10))
    save_data([x, y])

连续运行的输出结果

[] []
[9] [9]
[9, 10] [9, 9]
[9, 10, 2] [9, 9, 4]
[9, 10, 2, 5] [9, 9, 4, 1]
[9, 10, 2, 5, 6] [9, 9, 4, 1, 9]
[9, 10, 2, 5, 6, 10] [9, 9, 4, 1, 9, 1]

1
你真的想在load_data中使用open("bin.dat", "rb")。否则,如果二进制pickle流中有任何换行符,它将在Windows上出现错误。(而且它在3.x中不起作用,但是你的代码已经明确地使用2.x了。) - abarnert
谢谢sberry让我试试。 - Ale Rojas

0

很难确定,但我猜测你的问题是你正在编写一个二进制文件,然后尝试将其作为文本读取,而你在Windows上使用的是Python 2.x。

在这段代码中:

def LoadData(x,y):
    import pickle
    with open('bin.dat') as f:
        x,y = pickle.load(f)

如果二进制pickle序列中有任何LF换行符,将文件作为文本打开将会将它们转换为CR/LF对。这将导致pickle无效,因此会引发异常。
在这段代码中:
def Loader(x,y):
     try:
          LoadData(x,y)
     except:
          SaveData(x,y)

...你只需要吞咽任何异常并保存一些空值。

在这里,你可能只想处理文件未找到的错误(IOErrorOSErrorFileNotFoundError,具体取决于你使用的 Python 版本)。

但你肯定希望将异常放入变量中以帮助调试问题,像这样:

def Loader(x,y):
     try:
          LoadData(x,y)
     except Exception as e:
          SaveData(x,y)

你可以在调试器中在 SaveData 行上设置断点,或者只需添加一行 print(e) 并查看输出,以查看您为什么会到达那里。

同时,即使您修复了这个问题,LoadData也永远不会执行任何有用的操作。将x,y = pickle.load(f)分配给本地变量xy只是重新绑定它们。它们与Loader中的本地变量具有相同的名称并不意味着Loader的变量会发生更改。以前它们引用相同的值也是如此。

Python没有“引用变量”或“输出参数”。做这件事的正常方法就是返回要传回调用者的值:

def LoadData():
    import pickle
    with open('bin.dat') as f:
        x,y = pickle.load(f)
    return x,y

当然,Loader必须正确调用它:

def Loader(x,y):
     try:
          x,y = LoadData()
     except:
          SaveData(x,y)

而且你在Loader中再次遇到了完全相同的问题,所以你需要再次修复它,并在其调用者中修复。


谢谢您,abarnert!我想要做的是保存一个包含两个列表的列表。例如,一个列表将保存制造商(丰田,尼桑等),另一个列表将保存汽车型号(Tundra,Murano)。现在,每个元素都是一个对象。 - Ale Rojas
很少有关系是你试图保存一个由3个字符串组成的列表还是一个由分别包含7个和9个字符串的2个列表。无论哪种方式,列表都是单个对象,并且可以进行pickle处理,对吧?没有必要手动尝试显式地保存这两个列表,只需保存它们所在的列表即可。 - abarnert
同时,您是否想要每个品牌的车型列表,而不仅仅是所有车型和所有品牌的列表?换句话说,一个将品牌映射到车型列表的字典。 - abarnert
最后,看起来你可能想问一个跟进问题,但是如果是这样的话,我不明白你想问什么。请更明确地说明你正在做什么,你的期望以及实际发生的情况(最好通过编辑你的问题添加格式良好的代码和样本数据,或者提出一个新的问题,无论哪种方式更合适)。 - abarnert

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