使用 Python 创建 JSON

Use Python for Creating JSON

我想使用 Python 创建 JSON。

由于我找不到可以帮助我的库,我想知道是否可以检查 Python 文件中 类 的顺序?

例子

# example.py
class Foo:
    pass

class Bar:
    pass

如果我导入 example,我想知道 类 的顺序。在这种情况下,它是 [Foo, Bar] 而不是 [Bar, Foo]。

这可能吗?如果"yes",怎么办?

背景

我对 yaml/json 不满意。我有一个模糊的想法,通过 Python 类 创建配置(仅 类,不实例化对象)。

欢迎提供有助于我实现目标的答案(使用易于使用且有趣的工具创建 JSON)。

你可以用metaclass来记录每个class的创建时间,之后再根据它对class进行排序。

这适用于 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__))

inspect 模块可以告诉 class 声明的行号:

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

(将我的评论移至答案)

这是一个很棒的模糊想法。您应该 Figura 一个机会! 它正是这样做的。

(完全披露:我是 Figura 的作者。)

我应该指出声明的顺序在 Figura 中没有保留,在 json 中也没有。

我不确定 YAML 中的顺序保留,但我确实在 wikipedia 上找到了这个:

... according to the specification, mapping keys do not have an order

特定的 YAML 解析器可能会维护顺序,尽管它们不是必需的。

标准 json 模块易于使用,非常适合读写 JSON 配置文件。

对象在 JSON 结构中未排序,但 lists/arrays 是排序的,因此将依赖于顺序的信息放入列表中。

我已经使用 classes 作为配置工具,我所做的是从一个由特定 class 变量定制的基础 class 派生它们。通过像这样使用 class,我不需要工厂 class。例如:

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 class 这些工具可能不是正确的工具。

我用来创建 JSON 文件的另一个 python 工具是 Mako 模板系统。这是非常强大的。我们用它来将 IP 地址等变量填充到静态 JSON 文件中,然后由 C++ 程序读取这些文件。

首先,据我所知,您可以做两件事...

  1. 继续追求使用Python源文件作为配置文件。 (我不推荐这个,类似于用推土机打钉子或者把a shotgun转成轮子)
  2. 为配置文件切换到类似 TOML, JSON or YAML 的内容,这些文件专为作业而设计。

    JSON 或 YAML 中没有任何内容阻止它们保存 "ordered" 键值对。 Python 的 dict 数据类型默认是无序的(至少到 3.5),list 数据类型是有序的。使用默认加载器时,它们分别直接映射到 JSON 中的对象和数组。反序列化它们时只需使用 Python 的 OrderedDict 之类的东西,瞧,你保留了顺序!


除此之外,如果您真的想要使用 Python 源文件进行配置,我建议尝试使用 ast 模块。抽象语法树是语法级别分析的强大工具。

我编写了一个用于从文件中提取 class 行号和名称的快速脚本。

您(或真正的任何人)可以使用它或将其扩展到更广泛的范围,并根据需要进行更多检查。

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"
    ]
]

If I import example,

如果您要导入模块,example 模块位于导入路径中。导入就是执行anyPython模块中的example代码。这是一个相当大的安全漏洞——您在与应用程序其余部分相同的上下文中加载用户可编辑的文件。

我不确定这是否回答了您的问题,但它可能相关。看看优秀的 attrs 模块。它非常适合创建 类 用作数据类型。

这是来自 glyph's 博客(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",你会没事的。

我假设既然您关心保留 class-定义顺序,您也关心保留每个定义中的 顺序class.

值得指出的是,现在 pythonsince python3.6.

中的默认行为

另见 PEP 520:保留 Class 属性定义顺序

刚刚谈到关于从 python 创建 JSON 的要点。有一个名为 jsonpickle 的出色库,可让您将 python 个对象转储到 json。 (单独使用或与此处提到的其他方法一起使用,你可能会得到你想要的)