2017-04-06 37 views
5

此代碼:獲取一類的方法順序,這是在代碼

import inspect 

class Obj(): 

    def c(self): 
     return 1 

    def b(self): 
     return 2 

    def a(self): 
     return 3 

o = Obj() 

for name, value in inspect.getmembers(o, inspect.ismethod): 
    print str(value())+" "+name 

打印:

3 a 
2 b 
1 c 

由於inspect.getmembers回報在列表中的一個對象的所有成員(名稱,值)對按名稱排序,因爲您可以在https://docs.python.org/2/library/inspect.html#inspect.getmembers

中讀取但我想以與成員編寫代碼相同的順序獲取該列表,換句話說,輸出將是:

1 c 
2 b 
3 a 

有沒有辦法做到這一點?

感謝

+1

遺傳怎麼樣?如果'o'方法被定義在兩個不同的類中,它們應該以什麼順序進來? – user2357112

+2

[如何以與聲明相同的順序讀取類屬性]的可能重複項(http://stackoverflow.com/questions/4459531/how-to-read-class-attributes-in-the-same-order-as -declared) – pvg

+0

這是哪個Python版本? Python 2和3具有非常不同的解決方案(甚至小版本也有差異)。 –

回答

0

嗯,這是非常哈克,但基本上我直接檢查的來源和使用re找到方法名。儘管這個解決方案非常脆弱,但它並不涉及繼承,但它可能適用於您。假設我已將您的班級定義保存在名爲test.py的文件中:

>>> import test 
>>> import re 
>>> findmethods = re.compile(r" def (.+)\(") 
>>> findmethods.findall(inspect.getsource(test.Obj)) 
['c', 'b', 'a'] 
>>> 
+0

如果您擔心正則表達式失敗,可以在ast.parse(inspect.getsource(test.Obj))中輸入ast'併爲節點執行'name = [node.name。)body [0] .body if isinstance(node,ast.FunctionDef)]'。儘管如此,仍然依賴於具有可訪問源代碼的類,這不是一件確定的事情。如圖所示。 – Kevin

+0

@Kevin是的,絕對需要直接檢查源代碼的任何解決方案都必然是地獄般的,並且充滿了潛在的隱患。毫無疑問!但這是'ast'的一個很好的使用,使它不那麼脆弱! –

0

編號班級成員未訂購。他們被收集成字典,立即失去秩序。你可以使用像解析源代碼這樣的技巧,但它很容易中斷。對於初學者來說,源代碼不可用。

[編輯:看來python3允許更靈活地類的創建,從而能夠customize the way class members are gathered,如果你只python3,這可能是一個更好的辦法]

如果改變代碼是沒有問題的,你雖然可以使用一個裝飾:

import inspect 

def track_order(fn): 
    fn.order = track_order.idx 
    track_order.idx += 1 
    return fn 
track_order.idx = 0 

class Obj(object): 
    @track_order 
    def c(self): 
     return 1 

    @track_order 
    def b(self): 
     return 2 

    @track_order 
    def a(self): 
     return 3 

o = Obj() 

methods = sorted((item 
        for item in inspect.getmembers(o, inspect.ismethod)), 
       key=lambda item: item[1].order) 

for name, value in methods: 
    print str(value())+" "+name 

的裝飾增加了一個idx屬性,通過它的所有方法。 這使用了python具有一流功能的事實。

$ python test.py 
1 c 
2 b 
3 a 

注:這是使用Django的保持形狀和型號的軌道字段順序的方法。只有它們不需要裝飾器,因爲字段的類具有內置的實例化順序屬性(它被命名爲creation_counter)。

+0

使用meta-classes和'__prepare__'可用於Python 3,實際上有很多[很好的解決方案](http://stackoverflow.com/a/36060212/5014455)。在3.6中,成員將被排序自動! –

+0

@ juanpa.arrivillaga>確實很好的解決方案。可能對python3-only代碼更好。我在頂部添加了一條評論,但是我會讓我回答那些仍然需要保持與舊代碼兼容性的人:) – spectras

1

創建對象時,其所有屬性都包含在名爲__dict__的對象中的另一個專用屬性中,顧名思義就是一個普通的Python無序字典,因此它們不保證存儲在同樣的方式添加。當使用getmembers()檢索__dict__中的值時,Python在打印字典時自動重新組織字典,以使邏輯有意義。

爲了解決這個問題,必須做一些事情來將常規的Python字典__dict__變成某種有序的字典。

爲了簡單起見,我可以假定你正在使用Python 3。

使用collections軟件包,您可以獲得OrderedDict,這正是我們需要解決此類問題的技術。準備這個有序的字典,用於需要存儲訂購成員的類的元類,用於複製成員,最後在想要打印出所述成員時訪問這個新的OrderedDict

這可以在this Stack Overflow answer的行動中看到。

+0

Python 2.7沒有'__prepare__'。 – user2357112

+0

Woops打算輸出3.謝謝你的收穫。 –

1

cpython代碼被編譯爲虛擬機的字節碼。這些函數有一個__code__屬性,它是一個代碼對象。代碼對象具有co_firstlineno屬性,這是Python源代碼中的第一行。 (詳細請見inspect模塊。)

如果你知道你的方法都是源代碼,並且你知道你正在使用cpython,你可以使用它作爲排序鍵。但如果你不知道這些事情,它似乎很糟糕。

members = [ (name,meth) for name, meth in inspect.getmembers(o, inspect.ismethod)] 

members = sorted(members, key=lambda t: t[1].__code__.co_firstlineno) 

print '\n'.join(m[0] for m in members) 
+0

這幾乎可以工作,直到你介紹裝飾者。然後'co_firstlineno'來自完全不連接到裝飾函數定義位置的地方。 – user2357112

+0

是的。如果你有控制源,Wintro的可能是更好的答案。 –

相關問題