关于类:是否可以在Python中创建抽象类?

Is it possible to make abstract classes in Python?

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

我试图重新定义__new__(),就像这样:

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

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

1
2
class G(F):
    pass

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

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


使用abc模块创建抽象类。根据您的Python版本,使用abstractmethod装饰符声明方法抽象,并使用三种方法之一声明类抽象。

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

1
2
3
4
5
6
# Python 3.4+
from abc import ABC, abstractmethod
class Abstract(ABC):
    @abstractmethod
    def foo(self):
        pass
1
2
3
4
5
6
# Python 3.0+
from abc import ABCMeta, abstractmethod
class Abstract(metaclass=ABCMeta):
    @abstractmethod
    def foo(self):
        pass
1
2
3
4
5
6
7
8
# Python 2
from abc import ABCMeta, abstractmethod
class Abstract:
    __metaclass__ = ABCMeta

    @abstractmethod
    def foo(self):
        pass

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

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


当调用抽象方法时,实现这一点的老派方法(pre-pep 3119)只对抽象类中的raise NotImplementedError有效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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文档要简单得多。


这里有一个非常简单的方法,不用处理ABC模块。

在要成为抽象类的类的__init__方法中,可以检查self的"类型"。如果self的类型是基类,那么调用方正试图实例化基类,因此引发异常。下面是一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Base():
    def __init__(self):
        if type(self) is Base:
            raise Exception('Base is an abstract class and cannot be instantiated directly')
        # Any initialization code
        print('In the __init__  method of the Base class')

class Sub(Base):
    def __init__(self):
        print('In the __init__ method of the Sub class before calling __init__ of the Base class')
        super().__init__()
        print('In the __init__ method of the Sub class after calling __init__ of the Base class')

subObj = Sub()
baseObj = Base()

运行时,它产生:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
In the `__init__` method of the Sub class before calling `__init__` of the Base class

In the `__init__`  method of the Base class

In the `__init__` method of the Sub class after calling `__init__` of the Base class
Traceback (most recent call last):

  File"/Users/irvkalb/Desktop/Demo files/Abstract.py", line 16, in <module>
    baseObj = Base()

  File"/Users/irvkalb/Desktop/Demo files/Abstract.py", line 4, in `__init__`

    raise Exception('Base is an abstract class and cannot be instantiated directly')

Exception: Base is an abstract class and cannot be instantiated directly

这表明可以实例化从基类继承的子类,但不能直接实例化该基类。

伊尔夫


以前的大多数答案都是正确的,但下面是Python3.7的答案和示例。是的,您可以创建一个抽象类和方法。有时类应该定义一个逻辑上属于某个类的方法,但该类不能指定如何实现该方法。例如,在下面的父母和婴儿班中,他们都吃东西,但每个班的实施情况都不同,因为婴儿和父母吃不同种类的食物,他们吃东西的次数也不同。因此,EAT方法子类重写abstractClass.eat。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from abc import ABC, abstractmethod

class AbstractClass(ABC):

    def __init__(self, value):
        self.value = value
        super().__init__()

    @abstractmethod
    def eat(self):
        pass

class Parents(AbstractClass):
    def eat(self):
        return"eat solid food"+ str(self.value) +" times each day"

class Babies(AbstractClass):
    def eat(self):
        return"Milk only"+ str(self.value) +" times or more each day"

food = 3    
mom = Parents(food)
print("moms ----------")
print(mom.eat())

infant = Babies(food)
print("infants ----------")
print(infant.eat())

输出:

1
2
3
4
moms ----------
eat solid food 3 times each day
infants ----------
Milk only 3 times or more each day


这个将在python 3中工作

1
2
3
4
5
6
7
8
9
10
from abc import ABCMeta, abstractmethod

class Abstract(metaclass=ABCMeta):

    @abstractmethod
    def foo(self):
        pass

Abstract()
>>> TypeError: Can not instantiate abstract class Abstract with abstract methods foo

是的,您可以使用abc(抽象基类)模块在python中创建抽象类。

本网站将帮助您:http://docs.python.org/2/library/abc.html


这同样有效,而且很简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class A_abstract(object):

    def __init__(self):
        # quite simple, old-school way.
        if self.__class__.__name__ =="A_abstract":
            raise NotImplementedError("You can't instantiate this abstract class. Derive it, please.")

class B(A_abstract):

        pass

b = B()

# here an exception is raised:
a = A_abstract()

在代码片段中,还可以通过为子类中的__new__方法提供实现来解决这一问题,同样:

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

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

另外,当您创建一个新的(基本)类时,使它成为object的子类,如下所示:class MyBaseClass(object):。我不知道它是否有那么大的意义,但它有助于保持代码的样式一致性。


只需快速添加@timgilbert的老一套答案……您可以让抽象基类的init()方法引发异常,这样就不会实例化它了,不是吗?

1
2
3
4
5
6
7
8
9
>>> 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!