2017-03-12 60 views
1

我有一個特殊的statemachine在Python中實現,它使用類方法作爲狀態表示。我可以從Python中的classmethod派生嗎?

class EntityBlock(Block): 
    def __init__(self, name): 
    self._name = name 

    @classmethod 
    def stateKeyword1(cls, parserState : ParserState): 
    pass 

    @classmethod 
    def stateWhitespace1(cls, parserState : ParserState): 
    token = parserState.Token 
    if isinstance(token, StringToken): 
     if (token <= "generate"): 
     parserState.NewToken = GenerateKeyword(token) 
     parserState.NewBlock = cls(....) 
     else: 
     raise TokenParserException("....", token) 
    raise TokenParserException("....", token) 

    @classmethod 
    def stateDelimiter(cls, parserState : ParserState): 
    pass 

訪問GitHub的完整的源代碼關閉pyVHDLParser


當調試我的解析器FSM,我得到打印爲statenames:

State: <bound method Package.stateParse of <class 'pyVHDLParser.DocumentModel.Sequential.Package.Package'>> 

我想獲得更好的報告,所以我想覆蓋的每個邊界的__repr__默認行爲方法對象。

是的,我可以寫一個元類或申請第二個裝飾,但我質問自己:
是否有可能從classmethod派生並已例如被稱爲只有一個裝飾state

根據PyCharm的builtins.py(Python內置僞代碼的集合),classmethod是一個基於類的裝飾器。

+0

做這些classmethods的原因是什麼?它使得'EntityBlock'實際上是一個單例,但即使你只創建了一個實例,它也顯得更加複雜。特別是,如果你從不創建任何實例,'__init__'永遠不會被調用。如果你確實創建實例,它們將共享狀態,這似乎不太有用。 – cco

+0

這是一個最小的例子。 EntityBlock將被創建,否則它將不會有'__init__' ...不,它們不共享狀態,因爲「狀態」不是類的方法。該類是分層狀態機中的一組狀態。無論如何,它不回答我的問題,對吧? – Paebbels

+0

對。我的評論並不是針對你的問題,而只是對我看起來很奇怪的事情做出反應。 – cco

回答

1

是的,如果你願意,你可以編寫自己的課程,該課程從classmethod派生。雖然這有點複雜。您需要實現描述符協議(覆蓋classmethod的實現__get__),以便它返回另一個自定義類的實例,該實例類似於綁定方法對象。不幸的是,你不能繼承Python的內置綁定方法類型(我不知道爲什麼不)。

然後,最好的方法是將一個常規方法對象包裝在自定義類的實例中。我不確定你需要複製多少方法API,所以這可能會有點複雜。 (你需要你的狀態是否可以相互比較嗎?他們需要可水洗嗎?可以挑選嗎?)

無論如何,這裏是一個裸露的骨骼實現,它需要獲得工作方法所需的最小數量(加上新的repr ):

class MethodWrapper: 
    def __init__(self, name, method): 
     self.name = name if name is not None else repr(method) 
     self.method = method 

    def __call__(self, *args, **kwargs): 
     return self.method(*args, **kwargs) 

    def __repr__(self): 
     return self.name 

class State(classmethod): 
    def __init__(self, func): 
     self.name = None 
     super().__init__(func) 

    def __set_name__(self, owner, name): 
     self.name = "{}.{}".format(owner.__name__, name) 

    def __get__(self, owner, instance): 
     method = super().__get__(owner, instance) 
     return MethodWrapper(self.name, method) 

並在行動上快速預覽:

>>> class Foo: 
    @State 
    def foo(cls): 
     print(cls) 

>>> Foo.foo 
Foo.foo 
>>> Foo.foo() 
<class '__main__.Foo'> 
>>> f = Foo() 
>>> f.foo() 
<class '__main__.Foo'> 

注意,由State描述符中使用的__set_name__方法僅被Python 3.6調用。如果沒有這個新功能,描述符要學習它自己的名字會更困難(您可能需要製作一個裝飾工廠,將名稱作爲參數)。

+0

不需要比較,酸洗和散列。它是否對性能有影響?我的意思是調用方法/狀態? – Paebbels

+0

通過像我寫的方法包裝來調用方法肯定會比僅僅使用'classmethod'慢。它會將Python函數調用開銷兩次添加到已經執行了一次Python函數調用(對於方法的實際代碼)和兩次內置函數調用(對於'classmethod .__ get__'和方法對象的'__call__')的邏輯中。額外開銷的重要程度取決於其他代碼的複雜程度。如果你的大多數狀態方法在他們的身上只有「通過」,並且你的程序大部分時間都在調用它們,那麼性能可能會很大。 – Blckknght

相關問題