在Python中,序列化/反序列化类对象的最佳方法是什么?

3

我正在尝试从我正在处理的单元中记录一些数据,以进行长期和短期数据收集和存储。

我应该使用json库还是pickle库,然后如何从文件加载单元数据,能够操纵它,添加单位等,然后将更新后的数据保存回文件?

这里是我问题的详细说明以及我卡住的地方:

对于每个单元,我有一些类对象中的数据:

unit_1.name = 'unit_1'
unit_1.folder = 'C:\\Users\\Thomas\\Documents\\1'
unit_1.serial_number = 1
unit_1.offsets = [0.00, 0.00, 0.00, 0.00 ...]
unit_1.status = 'Open'
etc

我希望将这些对象长期存储(例如在文件中),但我还需要能够通过命令行访问它们作为类对象,方法是执行以下操作:

>python
Python 3.9.6 (tags/v3.9.6:db3ff76, Jun 28 2021, 15:26:21) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.

>from file import unit_log
>unit_log.load()
>unit_1.serial_number
1

我希望创建像这样的新单位:

>unit_2 = unit_log(2)

这样做会在类的init模块中创建其他字段。 之后,我可以通过执行以下操作来保存我创建或修改的单位:

>unit_log.save()

该类的代码如下,它将所有单元保存到文件中:

file.py

class unit_log:
    def __init__ (self, serial_number):
        self.serial_number = serial_number;
        self.unit_name = 'unit_'+str(serial_number)
        self.status = 'Open';
        self.offsets = [];
        self.folder = 'C:\\Users\\Thomas\\Documents\\' + str(serial_number)
    @classmethod
    def load (self):
        #Something that loads the data
    @classmethod
    def save (self):
        #Something that saves the data

我的理解是我正在尝试进行的操作称为序列化/反序列化,并且有几种不同的方法可以实现这一点(主要有pickle库和json库)。

我的问题是:如何将一个json文件(或pickle文件)反序列化为一系列类对象? 我假设json文件看起来像这样:

unit_logs.json
{
    unit_1:{
    {
    "unit_name": "unit_1",
    "serial_number": 1,
    "status": "Open",
    "offsets": [],
    "folder": "C:\\Users\\Thomas\\Documents\\1"
    }
    unit_2...
    unit_3...
    ...
}

我将使用log = json.load(unit_logs.json)来加载文件。然后,我希望有一个

for unit in log:
    unit["unit_name"] = unit_log(unit)

但这并不是声明变量的工作方式。

我认为pickle库的工作方式类似,但我对它不太熟悉。

我应该使用json库还是pickle库?如何从文件加载单位数据、能够操作它们、添加单位等,然后将更新后的数据保存回文件?


2
为什么要使用Pickle?你的JSON看起来很简单,使用JSON会比Pickle更快速。 - deadshot
我刚刚了解到pickle是另一种选择。但由于我对json更熟悉,我宁愿使用json。唯一的问题是我不知道如何将json文件反序列化成上述描述的类对象。 - Thomas K
3个回答

0

pickle 库使用方便

将对象 obj 转储到文件:

import pickle

with open(f_path, "wb") as f:
    pickle.dump(obj, f)

加载一个pickle文件:

with open(f_path, "rb") as f:
    obj = pickle.load(f)

然后您可以更改obj并将其再次写入文件

如果您想多次加载相同的pickle文件并具有不同的对象,则可以执行以下操作:

N=<number of copies>

with open(f_path, "rb") as f:
    obj_list = [pickle.load(f) for _ in range(N)]

现在您可以单独编辑obj_list中的每个对象,如果您想将其状态保存到pickle文件中,则可以像上面描述的那样使用pickle.dump
因此,您可以拥有一个“模板”pickle文件进行加载,然后根据需要编辑对象。

这并没有回答如何同时将所有单元写入加载模块的问题。 - Thomas K
什么意思?你的序列化对象也可以是对象列表。 - user107511
如果我正确理解你的代码,那么'obj'将是所有单元。我该如何更改一个单元?我想当你反序列化单元时,它们会以某种方式拥有自己的变量,这样你就可以做一些类似于'>unit_1.open_folder()'或者'unit_2.log_data()'的操作。如果它们没有各自的变量名,你还能怎样编写和操作特定的单元呢? - Thomas K
我一直在阅读,也许答案是为每个单位创建动态变量?不过我不太确定这在实践中如何运作。 - Thomas K
1
每次从pickle文件中加载时,您都会获得一个全新的不同对象,因此您可以拥有对象数组或将对象放入其他集合中。已在答案中添加。 - user107511

-1
这里有个你可能会觉得有用的东西。有一个Python软件包可智能构建来自YAML/JSON/dicts的对象,并正在积极开发和扩展。(完整披露,我是该软件包的共同作者,请参见此处编辑:

现在,在你的情况下,可以考虑按如下方式使用它:

将以下内容定义为一个YAML文件(filename.yaml)(或JSON):

name: 'unit_1'
folder: 'C:\\Users\\Thomas\\Documents\\1'
serial_number: 1
offsets: [0.00, 0.00, 0.00, 0.00 ]
status: 'Open'

将此作为Python对象从文件加载的方法如下:

>> python
Python 3.9.6 (tags/v3.9.6:db3ff76, Jun 28 2021, 15:26:21) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.

> from pickle_rick import PickleRick
> unit_1 = PickleRick('./filename.yaml')
> unit_1.serial_number
1

然后可以进行更改并再次保存到文件中:

> unit_1.serial_number += 41
> unit_1.serial_number
42
> unit_1.to_yaml_file('./filename.yaml')

或者作为JSON文件。

现在,如果要拥有多个单元,可以考虑使用以下结构:

units:
    - name: 'unit_1'
      folder: 'C:\\Users\\Thomas\\Documents\\1'
      serial_number: 1
      offsets: [0.00, 0.00, 0.00, 0.00 ]
      status: 'Open'

    - name: 'unit_2'
      folder: 'C:\\Users\\Thomas\\Documents\\1'
      serial_number: 1
      offsets: [0.00, 0.00 ]
      status: 'Closed'

然后这可以被加载为一个单位列表:

>> python
Python 3.9.6 (tags/v3.9.6:db3ff76, Jun 28 2021, 15:26:21) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.

> from pickle_rick import PickleRick
> all_units = PickleRick('./filename.yaml', deep=True)
> all_units.units[0].serial_number
1

原始帖子:

安装:

pip install pickle-rick

使用:

定义一个 YAML 或 JSON 字符串(或文件)。

BASIC:
 text: test
 dictionary:
   one: 1
   two: 2
 number: 2
 list:
   - one
   - two
   - four
   - name: John
     age: 20
 USERNAME:
   type: env
   load: USERNAME
 callable_lambda:
   type: lambda
   load: "lambda: print('hell world!')"
 datenow:
   type: lambda
   import:
     - "from datetime import datetime as dd"
   load: "lambda: print(dd.utcnow().strftime('%Y-%m-%d'))"
 test_function:
   type: function
   name: test_function
   args:
     x: 7
     y: null
     s: hello world
     any:
       - 1
       - hello
   import:
     - "math"
   load: >
     def test(x, y, s, any):
       print(math.e)
       iii = 111
       print(iii)
       print(x,s)
       if y:
         print(type(y))
       else:
         print(y)
       for i in any:
         print(i)

然后将其作为对象使用。

>> from pickle_rick import PickleRick

>> config = PickleRick('./config.yaml', deep=True, load_lambda=True)

>> config.BASIC.dictionary
{'one' : 1, 'two' : 2}

>> config.BASIC.callable_lambda()
hell world!

您可以定义Python函数,从其他文件或REST API加载额外的数据,环境变量,然后再将所有内容写入YAML或JSON。

在构建需要结构化配置文件的系统或笔记本中,这种方法特别有效。

使用此功能时要注意安全问题。仅加载受信任的文件,因为任何代码都可能被执行,请勿在不知道完整内容的情况下加载任何文件。

该软件包名为PickleRick,可在以下链接中获得:


你可以试着调整一下代码,使其能够至少远程匹配问题设置吗?虽然这看起来是 OP 想要做的可行的后端,但它似乎需要与 OP 已经发现的其他后端一样多的模板代码。 - MisterMiyagi

-1

如果你想使用 json,只需尝试这个:

json.dumps(unit_log_instance.__dict__)

你能详细说明如何将其实现到上述代码中吗?unit_log_instance是什么?这也没有回答如何一次性创建每个单元的类对象的问题。 - Thomas K

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