使用Python "json"模块使类可序列化

1 投票
1 回答
712 浏览
提问于 2025-04-16 23:44

我正在用Python编写一个Email()类,现在想把它扩展成一个SerializeEmail()类,这个新类主要增加两个方法,分别是.write_email()和.read_email()。我希望它能这样工作:

# define email
my_email = SerializeEmail()
my_email.recipients = 'link@hyrule.com'
my_email.subject = 'RE: Master sword'
my_email.body = "Master using it and you can have this."
# write email to file system for hand inspection
my_email.write_email('my_email.txt')
...
# Another script reads in email
my_verified_email = SerializeEmail()
my_verified_email.read_email('my_email.txt')
my_verified_email.send()

我已经了解了json的编码和解码过程,能够成功地写入我的SerializeEmail()对象,并且可以读取它。不过,我找不到一个满意的方法来通过SerializeEmail.read_email()来重新创建我的对象。

class SerializeEmail(Email):

    def write_email(self,file_name):

        with open(file_name,"w") as f:
            json.dump(self,f,cls=SerializeEmailJSONEncoder,sort_keys=True,indent=4)

    def read_email(self,file_name):

        with open(file_name,"r") as f:
           json.load(f,cls=SerializeEmailJSONDecoder)

问题在于,我的read_email()方法中的json.load()调用返回了一个SerializeEmail对象的实例,但这个对象并没有被赋值给我正在使用的当前实例。所以现在我必须做类似这样的事情:

another_email = my_verified_email.read_email('my_email.txt')

而我真正想要的是,当我调用my_verified_email.read_email()时,能够把文件中的数据填充到当前的my_verified_email实例里。我试过

self = json.load(f,cls=SerializeEmailJSONDecoder)

但那并不奏效。我可以把返回对象的每个元素逐个赋值给我的“self”对象,但这样做看起来很随意,也不优雅。我在寻找一种“正确的方法”来实现这个,如果有的话。有什么建议吗?如果你觉得我的整个思路有问题,并建议我用不同的方法来完成这个任务,请给我简单描述一下。

1 个回答

2

虽然你可以通过一些复杂的步骤把序列化的内容加载到一个已经存在的实例中,但我不建议这样做。这会让事情变得复杂,而且其实没有什么好处;每次你想从JSON加载一封邮件时,都需要额外创建一个临时实例。我的建议是使用一个工厂类或者工厂方法,它可以从序列化的JSON中加载邮件,并返回一个新的实例。我个人更喜欢工厂方法,下面是实现的方式:

class SerializeEmail(Email):

    def write_email(self,file_name):

        with open(file_name,"w") as f:
            json.dump(self,f,cls=SerializeEmailJSONEncoder,sort_keys=True,indent=4)

    @staticmethod
    def read_email(file_name):

        with open(file_name,"r") as f:
           return json.load(f,cls=SerializeEmailJSONDecoder)

# You can now create a new instance by simply doing the following:
new_email = SerializeEmail.read_email('my_email.txt')

注意这里的 @staticmethod 装饰器,它允许你在类上调用这个方法,而不需要传入隐含的第一个参数。通常情况下,工厂方法会是 @classmethod,但因为你是从JSON加载对象,所以不需要那个隐含的类参数。

你会发现,使用这个修改后,你不需要先实例化一个 SerializeEmail 对象,就可以直接从JSON加载另一个对象。你只需直接在类上调用这个方法,就能得到想要的效果。

撰写回答