2017-03-12 8 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

相关问题