2016-12-01 86 views
3

我想用Python创建JSON。使用Python创建JSON

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

# example.py 
class Foo: 
    pass 

class Bar: 
    pass 

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

这可能吗?如果“是”,如何?

背景

我不开心YAML/JSON。我有模糊的想法通过Python类创建配置(只有类,而不是实例化对象)。

帮助我实现目标的解决方案(使用易于使用的工具创建JSON)是值得欢迎的。

+1

这是一个很模糊的想法。你应该给[Figura](https://figura.readthedocs.io/en/latest/)一枪! – shx2

+0

我应该指出在Figura中不保存声明的顺序,[也不在json中](http://stackoverflow.com/a/7214312),也不在YAML中。 – shx2

+0

@ shx2 AFAIK yaml中的订单得到保存。否则,在saltstack中订购状态将无法运行:https://docs.saltstack.com/en/latest/ref/states/ordering。html – guettli

回答

10

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

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 
+0

这需要示例模块处于导入路径,并且是一个非常大的安全漏洞,在与其他应用程序相同的上下文中加载用户可编辑的文件,而没有任何类型的沙盒... – pradyunsg

+1

@pradyunsg:他写道“如果我导入示例”,所以导入似乎没有问题。 –

+0

噢......但是,我认为这是一个安全漏洞。在我的答案中添加了一个部分。 – pradyunsg

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__)) 
2

(移动我的评论答案)

这是一个伟大模糊的概念。你应该Figura一枪!它确实如此。

(全面披露:我菲古拉的作者)

我要指出声明的顺序不会保留菲古拉,也没有在JSON。

我不知道订单保存在YAML,但我没有找到这对wikipedia

...根据规范,映射键没有订单

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

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() 

每个人都应该使用正确的工具作业,所以也许蟒蛇类是不正确的工具,如果你需要订购

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

3

首先亮相,在我看来,有两件事情可以做...

  1. 继续追求使用Python源文件作为配置文件。 (我不推荐这样做。这是类似于使用推土机撞击钉子或转换a shotgun到车轮)
  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要导入的路径上。导入意味着在example模块中执行任意 Python代码。这是一个非常大的安全漏洞 - 您要在与应用程序其余部分相同的上下文中加载用户可编辑的文件。

+0

'ast'模块非常好,谢谢你的例子。我已经玩过了,直到我达到了一行:'ast.parse(ast.parse(open(“example.py”))中的n(n.lineno,n.name)。read() ))if isinstance(n,ast.ClassDef)]' –

+0

是的。这也起作用。我感到很傻。我最可能的回归是:我做了很多事情,因为我不确定OP是否只想要类名或其内部的任务。 (因为我也为它写了代码,然后删除了它) – pradyunsg

+0

'ast.NodeVisitor'和'ast.walk'都在树上走。 ast.walk对于这个单线程来说更简单,但对于更复杂的处理,ast.NodeVisitor可能更优雅。我只是更喜欢较短的例子,因为它更容易让读者理解。 –

1

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

下面是glyph's博客(扭曲的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”,你会没事的。

2

我假设,因为你在乎保留类定义为了,你还在乎每个类内保留秩序的定义。

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

Aslo see PEP 520:保留类属性定义顺序

+0

这是个好消息。到目前为止,我们仍然使用Python 2.7,但迟早我们会切换。谢谢。 – guettli

0

只需触及关于从python创建JSON的观点。有一个名为jsonpickle的优秀库,可以让你将python对象转储到json。 (并单独使用这个或与其他方法在这里提到的,你可能会得到你想要的)

+0

它是否也适用于课程?我没有物品。 – guettli

+0

它可以为班级工作,但它确实取决于您的需求。从我所看到的,将编码为类似:“PY /对象”:“ .B”,后来它可以解码它来重建B检查模块是入店 – Eytan