Используйте Python для создания JSON

Я хочу использовать Python для создания JSON.

Поскольку я не нашел никакой библиотеки, которая могла бы мне помочь, я хочу знать, возможно ли проверить порядок классов в файле Python?

пример

# example.py
class Foo:
    pass

class Bar:
    pass

Если я импортирую exampleЯ хочу знать порядок занятий. В данном случае это [Foo, Bar], а не [Bar, Foo].

Это возможно? Если "да", то как?

Фон

Я не доволен YAML / JSON. У меня есть смутная идея создать конфигурацию через классы Python (только классы, а не создание экземпляров для объектов).

Приветствуются ответы, которые помогают мне достичь цели (создать JSON с помощью простого и интересного инструмента).

8 ответов

Решение

Модуль inspect может сообщать номера строк объявлений класса:

import inspect

def get_classes(module):
    for name, value in inspect.getmembers(module):
        if inspect.isclass(value):
            _, line = inspect.getsourcelines(value)
            yield line, name

Итак, следующий код:

import example

for line, name in sorted(get_classes(example)):
    print line, name

Печать:

2 Foo
5 Bar

Во-первых, как я понимаю, есть 2 вещи, которые вы можете сделать...

  1. Продолжайте использовать исходные файлы Python в качестве файлов конфигурации. (Я не буду рекомендовать это. Это аналогично использованию бульдозера для удара по гвоздю или превращению ружья в колесо)
  2. Переключитесь на что-то вроде TOML, JSON или YAML для файлов конфигурации, которые предназначены для работы.

    Ничто в JSON или YAML не мешает им содержать "упорядоченные" пары ключ-значение. Питона dict тип данных неупорядочен по умолчанию (как минимум до 3.5) и list Тип данных упорядочен. Они отображаются непосредственно на объект и массив в JSON соответственно при использовании загрузчиков по умолчанию. Просто используйте что-то вроде Python OrderedDict при десериализации их и вуаля вы сохраняете порядок!


С учетом этого, если вы действительно хотите использовать исходные файлы Python для конфигурации, я предлагаю попробовать обработать файл, используя ast модуль. Абстрактные синтаксические деревья - это мощный инструмент для анализа уровня синтаксиса.

Я написал быстрый скрипт для извлечения номеров строк и имен классов из файла.

Вы (или кто-либо на самом деле) можете использовать его или расширить его, чтобы он был более обширным и имел больше проверок, если вы хотите что угодно.

import sys
import ast
import json


class ClassNodeVisitor(ast.NodeVisitor):

    def __init__(self):
        super(ClassNodeVisitor, self).__init__()
        self.class_defs = []

    def visit(self, node):
        super(ClassNodeVisitor, self).visit(node)
        return self.class_defs

    def visit_ClassDef(self, node):
        self.class_defs.append(node)


def read_file(fpath):
    with open(fpath) as f:
        return f.read()


def get_classes_from_text(text):
    try:
        tree = ast.parse(text)
    except Exception as e:
        raise e

    class_extractor = ClassNodeVisitor()

    li = []
    for definition in class_extractor.visit(tree):
        li.append([definition.lineno, definition.name])

    return li


def main():
    fpath = "/tmp/input_file.py"

    try:
        text = read_file(fpath)
    except Exception as e:
        print("Could not load file due to " + repr(e))
        return 1

    print(json.dumps(get_classes_from_text(text), indent=4))


if __name__ == '__main__':
    sys.exit(main())

Вот пример запуска следующего файла:

input_file.py:

class Foo:
    pass


class Bar:
    pass

Выход:

$ py_to_json.py input_file.py
[
    [
        1,
        "Foo"
    ],
    [
        5,
        "Bar"
    ]
]

Если я импортирую example,

Если вы собираетесь импортировать модуль, example модуль должен быть на пути импорта. Импорт означает выполнение любого кода Python в example модуль. Это довольно большая дыра в безопасности - вы загружаете редактируемый пользователем файл в том же контексте, что и остальная часть приложения.

Я предполагаю, что, поскольку вы заботитесь о сохранении порядка определения классов, вы также заботитесь о сохранении порядка определений внутри каждого класса.

Стоит отметить, что теперь это поведение по умолчанию в python, начиная с python3.6.

Также см. PEP 520: Сохранение порядка определения атрибутов класса.

(Перемещая мои комментарии к ответу)

Это отличная смутная идея. Ты должен дать Фигуре шанс! Это именно так.

(Полное раскрытие: я автор Фигуры.)

Следует отметить, что порядок деклараций не сохраняется в Figura, а также не в json.

Я не уверен насчет сохранения порядка в YAML, но я нашел это в википедии:

... согласно спецификации, ключи сопоставления не имеют порядка

Может случиться так, что определенные парсеры YAML поддерживают порядок, хотя они не обязаны это делать.

Я не уверен, что это ответ на ваш вопрос, но это может быть актуально. Взгляните на отличный модуль attrs. Он отлично подходит для создания классов для использования в качестве типов данных.

Вот пример из блога глифа (создатель Twisted Python):

import attr
@attr.s
class Point3D(object):
    x = attr.ib()
    y = attr.ib()
    z = attr.ib()

Это избавляет вас от написания большого количества стандартного кода - вы получаете такие вещи, как str представление и сравнение бесплатно, а модуль имеет удобный asdict функция, которую вы можете передать json библиотека:

>>> p = Point3D(1, 2, 3)
>>> str(p)
'Point3D(x=1, y=2, z=3)'
>>> p == Point3D(1, 2, 3)
True
>>> json.dumps(attr.asdict(p))
'{"y": 2, "x": 1, "z": 3}'

Модуль использует странное соглашение об именах, но читает attr.s как "attrs" и attr.ib как "attrib", и вы будете в порядке.

Вы можете использовать метакласс для записи времени создания каждого класса, а затем сортировать классы по нему.

Это работает в python2:

class CreationTimeMetaClass(type): 
    creation_index = 0
    def __new__(cls, clsname, bases, dct):
        dct['__creation_index__'] = cls.creation_index
        cls.creation_index += 1
        return type.__new__(cls, clsname, bases, dct)

__metaclass__ = CreationTimeMetaClass

class Foo: pass
class Bar: pass

classes = [ cls for cls in globals().values() if hasattr(cls, '__creation_index__') ]
print(sorted(classes, key = lambda cls: cls.__creation_index__))

Стандартный модуль json прост в использовании и хорошо работает для чтения и записи файлов конфигурации JSON.

Объекты не упорядочены в структурах JSON, но списки / массивы упорядочены, поэтому поместите информацию, зависящую от порядка, в список.

Я использовал классы в качестве инструмента конфигурации, и я выбрал их для получения из базового класса, который был настроен с помощью определенных переменных класса. Используя такой класс, я не нуждался в фабричном классе. Например:

from .artifact import Application
class TempLogger(Application): partno='03459'; path='c:/apps/templog.exe'; flag=True
class GUIDisplay(Application): partno='03821'; path='c:/apps/displayer.exe'; flag=False

в установочном скрипте

from .install import Installer
import app_configs

installer = Installer(apps=(TempLogger(), GUIDisplay()))
installer.baseline('1.4.3.3475')
print installer.versions()
print installer.bill_of_materials()

Нужно использовать правильные инструменты для работы, поэтому, возможно, классы Python не являются правильным инструментом, если вам нужно упорядочить.

Другой инструмент Python, который я использовал для создания файлов JSON, - это система шаблонов Mako. Это очень сильно. Мы использовали его для заполнения переменных, таких как IP-адреса и т. Д., В статические файлы JSON, которые затем считывались программами C++.

Просто коснемся вопроса о создании JSON из python. есть отличная библиотека под названием jsonpickle, которая позволяет вам выгружать объекты Python в json. (и используя это отдельно или с другими методами, упомянутыми здесь, вы, вероятно, можете получить то, что вы хотели)

Другие вопросы по тегам