我可以从python中的classmethod派生吗?

Can I derive from classmethod in Python?

我有一个用Python实现的特殊状态机,它使用类方法作为状态表示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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时,我得到的状态名打印为:

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

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

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

根据pycharm的builtins.py(python的builtins的伪代码集合),classmethod是一个基于类的修饰符。


是的,如果需要,您可以编写自己的类,该类从classmethod派生。不过有点复杂。您需要实现描述符协议(覆盖classmethod__get__实现),以便它返回另一个自定义类的实例,该实例的行为类似于绑定方法对象。不幸的是,您不能从Python的内置绑定方法类型继承(我不确定为什么不能)。

最好的方法可能是将一个普通方法对象包装在自定义类的实例中。不过,我不确定您需要复制多少方法API,所以这可能会变得有点复杂。(你需要你的国家相互比较吗?它们需要可散列吗?Picklable?)

无论如何,这里有一个简单的实现,它为获得一个工作方法(加上新的repr)做了最少的必要工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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)

以及它的快速演示:

1
2
3
4
5
6
7
8
9
10
11
12
>>> 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__方法仅由python3.6调用。如果没有这个新特性,描述符就很难学习它自己的名称(您可能需要创建一个以名称为参数的装饰器工厂)。