2016-12-01 82 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