如何在Python中使类或方法抽象?

我尝试像这样重新定义__new__():

class F:
    def __new__(cls):
        raise Exception("Unable to create an instance of abstract class %s" %cls)

但是现在,如果我创建一个从F继承的类G,像这样:

class G(F):
    pass

然后,我也不能实例化G,因为它调用它的超类的__new__方法。

是否有更好的方法来定义抽象类?


当前回答

老式的(pre-PEP 3119)方法是在调用抽象方法时在抽象类中引发NotImplementedError。

class Abstract(object):
    def foo(self):
        raise NotImplementedError('subclasses must override foo()!')

class Derived(Abstract):
    def foo(self):
        print 'Hooray!'

>>> d = Derived()
>>> d.foo()
Hooray!
>>> a = Abstract()
>>> a.foo()
Traceback (most recent call last): [...]

它没有像使用abc模块那样好的属性。您仍然可以实例化抽象基类本身,并且直到在运行时调用抽象方法时才会发现错误。

但是如果您处理的是一小组简单类,可能只有几个抽象方法,那么这种方法比费力地查阅abc文档要容易一些。

其他回答

我发现公认的答案和其他答案都很奇怪,因为它们把self传递给了一个抽象类。抽象类没有实例化,所以不能有self。

试试这个吧,很管用。

from abc import ABCMeta, abstractmethod


class Abstract(metaclass=ABCMeta):
    @staticmethod
    @abstractmethod
    def foo():
        """An abstract method. No need to write pass"""


class Derived(Abstract):
    def foo(self):
        print('Hooray!')


FOO = Derived()
FOO.foo()

使用abc模块创建抽象类。使用abstractmethod装饰器来声明方法抽象,并使用三种方法之一来声明类抽象,具体取决于您的Python版本。

在Python 3.4及以上版本中,可以从ABC继承。在早期版本的Python中,需要将类的元类指定为ABCMeta。指定元类在Python 3和Python 2中有不同的语法。三种可能性如下所示:

# Python 3.4+
from abc import ABC, abstractmethod
class Abstract(ABC):
    @abstractmethod
    def foo(self):
        pass
# Python 3.0+
from abc import ABCMeta, abstractmethod
class Abstract(metaclass=ABCMeta):
    @abstractmethod
    def foo(self):
        pass
# Python 2
from abc import ABCMeta, abstractmethod
class Abstract:
    __metaclass__ = ABCMeta

    @abstractmethod
    def foo(self):
        pass

无论使用哪种方式,都不能实例化具有抽象方法的抽象类,但可以实例化提供这些方法的具体定义的子类:

>>> Abstract()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Abstract with abstract methods foo
>>> class StillAbstract(Abstract):
...     pass
... 
>>> StillAbstract()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class StillAbstract with abstract methods foo
>>> class Concrete(Abstract):
...     def foo(self):
...         print('Hello, World')
... 
>>> Concrete()
<__main__.Concrete object at 0x7fc935d28898>

在你的代码片段中,你也可以通过在子类中提供__new__方法的实现来解决这个问题,类似地:

def G(F):
    def __new__(cls):
        # do something here

但这是一个黑客,我建议你不要这样做,除非你知道你在做什么。对于几乎所有的情况,我建议您使用abc模块,在我之前的其他人已经建议过了。

同样,当你创建一个新的(基)类时,让它成为子类对象,就像这样:我不知道它是否还有那么重要,但它有助于保持代码的风格一致性

老式的(pre-PEP 3119)方法是在调用抽象方法时在抽象类中引发NotImplementedError。

class Abstract(object):
    def foo(self):
        raise NotImplementedError('subclasses must override foo()!')

class Derived(Abstract):
    def foo(self):
        print 'Hooray!'

>>> d = Derived()
>>> d.foo()
Hooray!
>>> a = Abstract()
>>> a.foo()
Traceback (most recent call last): [...]

它没有像使用abc模块那样好的属性。您仍然可以实例化抽象基类本身,并且直到在运行时调用抽象方法时才会发现错误。

但是如果您处理的是一小组简单类,可能只有几个抽象方法,那么这种方法比费力地查阅abc文档要容易一些。

只是对@TimGilbert老派回答的一个快速补充…你可以让你的抽象基类的init()方法抛出一个异常,这将阻止它被实例化,不是吗?

>>> class Abstract(object):
...     def __init__(self):
...         raise NotImplementedError("You can't instantiate this class!")
...
>>> a = Abstract()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
NotImplementedError: You can't instantiate this class!