Python 2.7结合abc.abstractmethod和classmethod

jch*_*chu 26 python google-app-engine python-2.7

如何在Python 2.7中为抽象类方法创建装饰器?

是的,这与这个问题类似,除了我想要结合abc.abstractmethodclassmethod不是staticmethod.此外,它看起来像是在Python 3abc.abstractclassmethod添加的(我认为?),但我使用的是Google App Engine,因此我目前仅限于Python 2.7

提前致谢.

Ray*_*ger 28

这是一个从Python 3.3的abc模块中的源代码派生的工作示例:

from abc import ABCMeta

class abstractclassmethod(classmethod):

    __isabstractmethod__ = True

    def __init__(self, callable):
        callable.__isabstractmethod__ = True
        super(abstractclassmethod, self).__init__(callable)

class DemoABC:

    __metaclass__ = ABCMeta

    @abstractclassmethod
    def from_int(cls, n):
        return cls()

class DemoConcrete(DemoABC):

    @classmethod
    def from_int(cls, n):
        return cls(2*n)

    def __init__(self, n):
        print 'Initializing with', n
Run Code Online (Sandbox Code Playgroud)

这是运行时的样子:

>>> d = DemoConcrete(5)             # Succeeds by calling a concrete __init__()
Initializing with 5

>>> d = DemoConcrete.from_int(5)    # Succeeds by calling a concrete from_int()
Initializing with 10

>>> DemoABC()                       # Fails because from_int() is abstract    
Traceback (most recent call last):
  ...
TypeError: Can't instantiate abstract class DemoABC with abstract methods from_int

>>> DemoABC.from_int(5)             # Fails because from_int() is not implemented
Traceback (most recent call last):
  ...
TypeError: Can't instantiate abstract class DemoABC with abstract methods from_int
Run Code Online (Sandbox Code Playgroud)

请注意,最后一个示例失败,因为cls()不会实例化. ABCMeta防止过早实例化未定义所有必需抽象方法的类.

调用from_int()抽象类方法时触发失败的另一种方法是让它引发异常:

class DemoABC:

    __metaclass__ = ABCMeta

    @abstractclassmethod
    def from_int(cls, n):
        raise NotImplementedError
Run Code Online (Sandbox Code Playgroud)

设计ABCMeta不会阻止在未实例化的类上调用任何抽象方法,因此您可以通过调用cls()通常的类方法或引发NotImplementedError来触发失败.无论哪种方式,你都会得到一个漂亮,干净的失败.

编写描述符来拦截对抽象类方法的直接调用可能很诱人,但这与ABCMeta的整体设计不一致(这是关于在实例化之前检查所需方法而不是在调用方法时) .

  • 检查Python源代码是个好主意,但不幸的是,类似于我写`classmethod`,然后是`abc.abstractmethod`时,这并没有强制执行`abstractmethod`.即我仍然可以调用该方法. (2认同)
  • @jchu ABCMeta是关于在缺少抽象方法时阻止类实例化的.它没有代码来阻止对未实例化的类上的抽象方法的调用.如果你想在调用抽象类方法时出现干净的失败,那么让它引发*NotImplementedError*或让它尝试用``cls()``实例化. (2认同)

dmy*_*tro 20

另一种可行的解决方法

class A:
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def some_classmethod(cls):
        """IMPORTANT: this is class method, override it with @classmethod!"""
        pass

class B(A):
    @classmethod
    def some_classmethod(cls):
        print cls
Run Code Online (Sandbox Code Playgroud)

现在,在实现"some_classmethod"之前,仍然无法从A实例化,并且如果使用classmethod实现它,它就可以工作.


cow*_*tor 6

您可以升级到Python 3

与开始的Python 3.3,它是可以组合 @classmethod@abstractmethod

import abc
class Foo(abc.ABC):
    @classmethod
    @abc.abstractmethod
    def my_abstract_classmethod(...):
        pass
Run Code Online (Sandbox Code Playgroud)

感谢@gerrit 向我指出这一点。