Как создать JSON-кодируемую динамическую строку в Python - PullRequest
3 голосов
/ 01 мая 2019

Я хочу создать объект, который «действует» как строка, но при доступе к нему запускает функцию и возвращает этот результат.

Простой случай для __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 оценивает объект, игнорируя пользовательские методы обработки данных.

Я не могу использовать пользовательский JSONEncoder, так как этот объект предназначен для отправки через модули, к которым у меня нет прав на изменение. Есть идеи?

ОБНОВЛЕНИЕ ДЛЯ ЯСНОСТИ:

Ожидаемый результат

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

т.е. «Точно так же, как если бы это была обычная строка, но основанная на текущем времени»

Ответы [ 2 ]

0 голосов
/ 07 мая 2019

Если все, что вам нужно сделать, это отобразить его как объект 'динамической строки' при вызове json.dumps(), первая часть моего ответа на вопрос Создание объекта JSON, сериализуемого с регулярным Можно использовать кодировщик , если вы не извлекаете свой класс из встроенного класса str и добавляете метод to_json.

Изменение первого параметра препятствует тому, чтобы модуль json автоматически обрабатывал его, потому что строки - и любой его подкласс - относятся к типам вещей, которые жестко кодируется для обработки ).

# 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"
0 голосов
/ 01 мая 2019

Проблема с этим, кажется, связана с тем фактом, что класс str является неизменным, который получает свое значение от инициации вашего объекта и не изменяется.Вызывается функция __new__ класса str, которая принимает 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 просто неизменяем ирасширение, родительская строка ваших объектов также неизменна на весь срок жизни вашего экземпляра ..

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...