我可以从Python中的classmethod派生出来吗?

时间:2017-03-12 01:13:57

标签: python-3.x python-decorators class-method

我在Python中实现了一个特殊的statemachine,它使用类方法作为状态表示。

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时,我将状态名打印为:

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

我想获得更好的报告,所以我想覆盖每个绑定方法对象的__repr__的默认行为。

是的,我可以写一个元类或应用第二个装饰,但我在质疑自己:
是否可以从classmethod派生并且只有一个装饰器被调用,例如state

根据PyCharm的builtins.py(Python内置函数的一组虚拟代码),classmethod是一个基于类的装饰器。

1 个答案:

答案 0 :(得分:1)

是的,如果需要,您可以编写自己的类classmethod。虽然这有点复杂。您需要实现描述符协议(覆盖classmethod的{​​{1}}的实现),以便它返回另一个自定义类的实例,其行为类似于绑定方法对象。不幸的是,你不能继承Python的内置绑定方法类型(我不知道为什么不这样做)。

然后,最好的方法可能是将一个普通方法对象包装在自定义类的实例中。我不确定你需要复制多少方法API,所以这可能会有点复杂。 (你需要你的州可以相互比较吗?他们需要是可以洗的吗?可选吗?)

无论如何,这是一个简单的实现,它实现了获得工作方法所需的最小量(加上新的__get__):

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'> 描述符使用的__set_name__方法仅由Python 3.6调用。如果没有这个新功能,描述符将更难以学习它自己的名称(您可能需要创建一个以名称作为参数的装饰器工厂)。