我可以让 json.dumps/loads 使用 Encoder/Decoder 个实例而不是 类 吗?

Can I make json.dumps/loads work with Encoder/Decoder instances instead of classes?

请考虑这个自定义 JSON-编码器的玩具示例。

import json
from typing import Any

class MyNumber:
    def __init__(self, num):
        self.num = num

class MultEncoder(json.JSONEncoder):
    'Multiply MyNumber instances when JSON-encoding.'

    def default(self, o: Any) -> Any:
        if isinstance(o, MyNumber):
            return o.num*2
        return super().default(o)

它是这样工作的:

>>> json.dumps({"a": MyNumber(5)}, cls=MultEncoder)
'{"a": 10}'

我的问题是如何使因子动态化?我想做:

class MultEncoder(json.JSONEncoder):
    'Multiply MyNumber instances when JSON-encoding.'
    
    def __init__(self, factor, *args, **kwargs):
        self.factor = factor        
        super().__init__(*args, **kwargs)

    def default(self, o: Any) -> Any:
        if isinstance(o, MyNumber):
            return o.num*self.factor
        return super().default(o)

当然,json.dumps({"a": MyNumber(5)}, cls=MultEncoder(7)) 失败了

TypeError: 'MultEncoder' object is not callable

因为 cls 参数应该是一个 class,而不是一个实例。

edit1:请注意,我无法编写自定义 my_json_dumps/my_json_loads 函数,我只能控制在代码的其他部分导入哪些 encoder/decoder classes其中使用 json.dumpsloads

edit2:请注意,我试图举一个通用的简单示例。在我的真实代码中,encoder/decoder 需要知道连接到数据库和其他动态配置的凭据。

我想出了一个非常丑陋的解决方案:一个 class-factory 函数,其中返回的 class 访问 closure-variables.

def MultEncoder(factor):
    class _MultEncoder(json.JSONEncoder):
        'Multiply MyNumber instances when JSON-encoding.'

        def default(self, o: Any) -> Any:
            if isinstance(o, MyNumber):
                return o.num*factor
            return super().default(o)

    return _MultEncoder

演示:

>>> json.dumps({"a": MyNumber(5)}, cls=MultEncoder(7))
'{"a": 35}'

这是我们在这里能做的最好的吗?

只需添加 __call__ 方法即可使 MultEncoder class 实例可调用。

class MyNumber:
    def __init__(self, num):
        self.num = num
class MultEncoder(json.JSONEncoder):
    'Multiply MyNumber instances when JSON-encoding.'
    
    def __init__(self, factor, *args, **kwargs):
        self.factor = factor        
        super().__init__(*args, **kwargs)
    def __call__(self,**kwargs):
        return self

    def default(self, o: Any) -> Any:
        if isinstance(o, MyNumber):
            return o.num*self.factor
        return super().default(o)
json.dumps({"a": MyNumber(5)}, cls=MultEncoder(7))

输出为:

'{"a": 35}'