Используйте 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 вещи, которые вы можете сделать...
- Продолжайте использовать исходные файлы Python в качестве файлов конфигурации. (Я не буду рекомендовать это. Это аналогично использованию бульдозера для удара по гвоздю или превращению ружья в колесо)
Переключитесь на что-то вроде TOML, JSON или YAML для файлов конфигурации, которые предназначены для работы.
Ничто в JSON или YAML не мешает им содержать "упорядоченные" пары ключ-значение. Питона
dict
тип данных неупорядочен по умолчанию (как минимум до 3.5) иlist
Тип данных упорядочен. Они отображаются непосредственно на объект и массив в JSON соответственно при использовании загрузчиков по умолчанию. Просто используйте что-то вроде PythonOrderedDict
при десериализации их и вуаля вы сохраняете порядок!
С учетом этого, если вы действительно хотите использовать исходные файлы 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. (и используя это отдельно или с другими методами, упомянутыми здесь, вы, вероятно, можете получить то, что вы хотели)