使用 JSON 在 Python 中创建 class 对象的最佳方法

Best way to use a JSON to create class objects in Python

我一直在练习使用 JSON 文件和 OOP,所以我想我会同时做这两者,但我一直在努力寻找最有效的方法来创建 class 个基于 JSON 个部分的对象。经过大量询问,我发现使用 ** 效果最好,尽管我根本不知道它是如何工作的。代码是:

import json

class Warship(object):
    def __init__(self, name, gun_config, top_speed, belt_armor, displacement):
        self.name = name
        self.gun_config = gun_config
        self.top_speed = top_speed
        self.belt_armor =belt_armor
        self.displacement = displacement
    
    def __repr__(self):
        return f'''{self.name}, {self.gun_config}, {self.top_speed} knts, {self.belt_armor} mm, {self.displacement} tons'''


workfile = json.load(open('warships.json', 'r'))

bisc = Warship(**workfile['kms'][0])
tirp = Warship(**workfile['kms'][1])
nc = Warship(**workfile['usn'][0])
wa = Warship(**workfile['usn'][1])

这是我为这个练习制作的 JSON 文件:

{
    "kms": [
        {
            "name": "Bismarck",
            "gun_config": "3x2",
            "top_speed": "29",
            "belt_armor": "320",
            "displacement": "41700"
        },
        {
            "name": "Tirpitz",
            "gun_config": "3x2",
            "top_speed": "30",
            "belt_armor": "320",
            "displacement": "41700"
        }
    ],
    "usn": [
        {
            "name": "North Carolina",
            "gun_config": "3x3",
            "top_speed": "28",
            "belt_armor": "305",
            "displacement": "36600"
        },
        {
            "name": "Washington",
            "gun_config": "3x3",
            "top_speed": "29",
            "belt_armor": "305",
            "displacement": "36600"
        }
    ]
    
}

像这样使用 ** 是基于 JSON 对象创建 Python class 对象的最佳方法吗?还是有更好的方法?

使用 ** 运算符可能是首选方法,只要您的构造函数参数与 JSON 属性的名称完全匹配(就像它们在您的示例中所做的那样)并且只要构造函数上没有可能造成严重破坏的额外参数:

def Message:
    def __init__(greeting: str, wipe_c_drive: bool = False):
        self.greeting = greeting
        if wipe_c_drive:
            shutil.rmtree('C:/')


workfile = json.load(open('greetings.json', 'r'))
hello = Message(**workfile['hello'])

greetings.json中的数据:

{
  "hello": {
    "greeting": "Hello there!"
  }
}

可能会出什么问题,对吗?

** 运算符是一个 'spreading' 运算符,它采用像字典一样的 key/value 对数据对象,并将其传播 - 通常为函数提供关键字参数:

def say(greeting, name):
    print(f'{greeting} to you, {name}!')


d = {'greeting': 'hello', 'name': 'AnFa')
say(**d)

这也适用于您的情况,因为 JSON 对象作为字典加载,因此可以使用 ** 运算符进行传播。由于您的 JSON 对象的属性与构造函数的关键字参数的名称完全匹配,因此它有效。

类似地,* 运算符展开一个列表:

xs = ['hello', 'AnFa']
say(*xs)

结果相同,但尝试将值作为位置参数传递。

了解了以上,你或许也明白了为什么有时候会看到这个:

def some_func(x, *args, **kwargs):
    print(x)
    some_other_func(*args, **kwargs)

此处,*args**kwargs 用于捕获 args(参数)和 kwargs(关键字参数)中的位置参数和关键字参数,第二次,他们将它们传播回 some_other_func 的调用中。这些名称 argskwargs 并不神奇,它们只是约定俗成。

如果你想要风险较小的东西(由于上面解释的原因),你可以提供 class 'to_json' 和 'from_json' 方法,但很快事情就会得到复杂,您可能想查看现有的库,正如用户@LeiYang 所建议的那样(其中有很多)。