【问题标题】:How to create a JSON encodable dynamic string in Python如何在 Python 中创建 JSON 可编码的动态字符串
【发布时间】:2019-09-20 01:00:57
【问题描述】:

我想创建一个像字符串一样“行为”的对象,但是当它被访问时,会触发一个函数并返回该结果。

__str____repr__ 的简单案例很简单;但我不知道如何让json.dumps 以同样的方式对待它;

import json, datetime


class DynamicString(str):
    def __init__(self, genf):
        self.generate = genf
    def __repr__(self):
        print("Called Repr")
        return self.generate()
    def __str__(self):
        print("Called Str")
        return self.generate()


dater=DynamicString(lambda: datetime.datetime.now().isoformat())
print(f"{dater!s}, {dater!r}, {dater!a}")
>>> Called Str
>>> Called Repr
>>> Called Repr
>>> 2019-05-01T13:52:12.588907, 2019-05-01T13:52:12.588933, 2019-05-01T13:52:12.588950

print(json.dumps(dater))
>>> "<function <lambda> at 0x10bb48730>"

似乎json.dumps 正在评估它忽略自定义 dunder 方法的对象。

我不能使用自定义JSONEncoder,因为该对象旨在通过我无权修改的模块发送。有任何想法吗?

澄清更新:

预期输出

json.dumps(dater)
>>> '"2019-05-01T16:43:21.956985"'

即"就好像它只是一个普通的字符串,但是基于当前时间"

【问题讨论】:

  • 您从str 继承是否有特殊原因未在此处显示?或者你可以从其他东西继承吗?
  • 你不能json.dumps(str(dater))吗?或者构建自己的序列化方法以防出现更复杂的行为?
  • @Error-SyntacticalRemorse:在dumps 中更改default 将不起作用,因为default 仅用于JSONEncoder 尚不知道如何处理的对象(请参阅@ 987654323@)。换句话说,它不会被使用,因为isinstance(dater, str)True。它适用于您链接到的答案,因为类 Doc 不是从表中显示的内容派生的。
  • 支持:从您的最后评论来看,听起来您的类不需要从str 派生,只是或多或少像一个。如果这是真的,那可能还有希望。但是,您还没有说出在生成的 JSON 输出上使用 loads() 后想要或期望得到什么。

标签: python json string serialization


【解决方案1】:

这个问题似乎在于str 类是不可变的,它从你的对象的初始化中获取它的值,并且不会改变。调用str 类的__new__ 函数,它接收您的genf,然后评估为

"<function <lambda> at 0xADDRESS>"

对实例的所有后续调用对该字符串没有影响。要验证这一点,您可以通过覆盖 __new__ 来覆盖类定义开头的字符串:

class DynamicString(str):
    def __new__(cls, genf):
        o = super(DynamicString, cls).__new__(cls, "HAHA")
        return o
    ... rest of your class ...

... rest of your script ...

# Then:
>>> json.dumps(dater)
>>> "HAHA"

似乎没有任何解决方法,因为str 只是不可变的,并且通过扩展,您的对象父字符串在您的实例的生命周期内也是不可变的..

【讨论】:

  • 我不认为问题在于不变性,这是因为JSONEncoder 已经知道如何处理str 类型的对象,包括从它派生的任何类(即str 子类)。文档中有一个table,显示了它自动处理的对象类型。
  • 关键是,当它这样做时,它会自动采用在创建实例时启动的不可变字符串。没有办法改变它,所以剩下的唯一选择就是使用像str(instance)这样的默认函数。
【解决方案2】:

如果您只想在调用json.dumps() 时将其显示为“动态字符串”对象,则可以使用我的answer 到问题Making object JSON serializable with regular encoder 的第一部分,前提是您don' t 从内置的 str 类派生您的类并添加一个 to_json 方法。

更改前者会阻止 json 模块自动处理它,因为字符串(以及它的任何子类)属于 hardcoded to handle 的类型)。

# Monkey-patch json module.
from json import JSONEncoder

def _default(self, obj):
    return getattr(obj.__class__, "to_json", _default.default)(obj)

_default.default = JSONEncoder.default  # Save unmodified default.
JSONEncoder.default = _default # Replace it.


if __name__ == '__main__':

    # Sample usage.

    import datetime
    import json


    class DynamicString(object):
        def __init__(self, genf):
            self.generate = genf

        def __repr__(self):
            print("Called Repr")
            return self.generate()

        def __str__(self):
            print("Called Str")
            return self.generate()

        def to_json(self):  # Added.
            return repr(self)


    dater = DynamicString(lambda: datetime.datetime.now().isoformat())
    print(f"{dater!s}, {dater!r}, {dater!a}")
    print()
    print(json.dumps(dater))

输出:

Called Str
Called Repr
Called Repr
2019-05-07T13:11:32.061129, 2019-05-07T13:11:32.061129, 2019-05-07T13:11:32.061129

Called Repr
"2019-05-07T13:11:32.061129"

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-20
    • 1970-01-01
    • 2021-12-05
    • 1970-01-01
    • 2017-03-21
    • 2012-04-20
    • 2021-11-27
    • 1970-01-01
    相关资源
    最近更新 更多