jch*_*chu 26 python google-app-engine python-2.7
如何在Python 2.7中为抽象类方法创建装饰器?
是的,这与这个问题类似,除了我想要结合abc.abstractmethod而classmethod不是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的整体设计不一致(这是关于在实例化之前检查所需方法而不是在调用方法时) .
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实现它,它就可以工作.
您可以升级到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 向我指出这一点。