3

我想使用 Python 创建 JSON。

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

例子

# example.py
class Foo:
    pass

class Bar:
    pass

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

这可能吗?如果“是”,怎么做?

背景

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

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

4

8 回答 8

9

检查模块可以告诉类声明的行号:

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
于 2016-12-05T16:37:52.370 回答
3

首先,在我看来,你可以做两件事......

  1. 继续追求使用 Python 源文件作为配置文件。(我不会推荐这个。这类似于使用推土机敲钉子或将霰弹枪转换为轮子)
  2. 切换到TOMLJSONYAML之类的配置文件,这些文件专为工作而设计。

    JSON 或 YAML 中没有任何内容可以阻止它们持有“有序”的键值对。Python 的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。这是一个相当大的安全漏洞——您在与应用程序的其余部分相同的上下文中加载用户可编辑的文件。

于 2016-12-06T17:45:28.523 回答
2

(将我的评论移至答案)

这是一个非常模糊的想法。你应该菲古拉一个机会! 它正是这样做的。

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

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

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

...根据规范,映射键没有顺序

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

于 2016-12-05T20:03:39.963 回答
2

我假设由于您关心保留类定义顺序,因此您还关心保留每个类中定义的顺序

值得指出的是,现在是 python 中的默认行为因为 python3.6

另请参阅PEP 520:保留类属性定义顺序

于 2016-12-09T05:29:50.573 回答
1

您可以使用元类来记录每个类的创建时间,然后按它对类进行排序。

这适用于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__))
于 2016-12-01T10:11:13.700 回答
1

标准的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 类可能不是正确的工具。

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

于 2016-12-06T00:33:00.523 回答
1

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

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

于 2016-12-09T05:06:20.013 回答
0

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

于 2016-12-10T12:21:16.423 回答