python中的抽象方法

Abstract methods in Python

本问题已经有最佳答案,请猛点这里访问。

我在使用Python继承时遇到问题。虽然这个概念对我来说太简单了,但到目前为止,我在Python一直无法理解,这至少让我感到惊讶。

我有一个原型:

1
2
3
4
5
6
7
class Shape():
   def __init__(self, shape_name):
       self.shape = shape_name

class Rectangle(Shape):
   def __init__(self, name):
       self.shape = name

在上面的代码中,我如何才能创建一个需要为所有子类实现的抽象方法?


在介绍ABC之前,你会经常看到这一点。

1
2
3
4
5
6
7
8
class Base(object):
    def go(self):
        raise NotImplementedError("Please Implement this method")


class Specialized(Base):
    def go(self):
        print"Consider me implemented"


沿着这些线,用ABC

1
2
3
4
5
6
7
8
9
import abc

class Shape(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def method_to_implement(self, input):
       """Method documentation"""
        return

另请阅读本教程:http://www.doughellmann.com/pymotw/abc/

您还可以查看zope.interface,它是在python中引入abc之前使用的。

  • http://pypi.python.org/pypi/zope.interface
  • http://wiki.zope.org/interfaces/frontpage


参见ABC模块。基本上,在类上定义__metaclass__ = abc.ABCMeta,然后用@abc.abstractmethod修饰每个抽象方法。除非重写了所有抽象方法,否则无法实例化从此类派生的类。

如果您的类已经在使用一个元类,那么从ABCMeta而不是type派生它,您可以继续使用自己的元类。

一个便宜的选择(以及引入abc模块之前的最佳实践)是让所有抽象方法只引发一个异常(NotImplementedError是一个很好的异常),这样从它派生的类就必须重写该方法才能有用。

但是,abc解决方案更好,因为它可以防止此类类被实例化(即它"更快地失败"),而且还因为您可以提供在派生类中使用super()函数可以实现的每个方法的默认或基本实现。


你不能用语言原语。正如前面提到的,abc包在python2.6和更高版本中提供了这个功能,但是没有python2.5和更低版本的选项。abc包不是Python的一个新特性;相反,它通过添加显式的"这个类说它这样做了吗?"检查,手动实现一致性检查,如果此类声明是错误的,则在初始化期间会导致错误。

python是一种激进的动态类型语言。它不指定语言原语以允许您阻止程序编译,因为对象与类型要求不匹配;这只能在运行时发现。如果您需要一个子类实现一个方法,请将其记录下来,然后盲目地调用该方法,希望它会出现在那里。

如果它在那里,太棒了,它就简单地工作了;这被称为duck-typing,并且您的对象已经像duck一样发出足够的震动来满足接口的需要。即使您正在调用这样一个方法的对象是self,为了强制重写的目的,这也可以很好地工作,因为基本方法需要特定的特性实现(通用函数),因为self是一个约定,而不是任何实际特殊的东西。

异常出现在__init__中,因为当调用初始值设定项时,派生类型的初始值设定项没有,因此它还没有机会将自己的方法钉在对象上。

如果没有实现该方法,您将得到一个attributeError(如果它根本不存在)或一个typeError(如果有同名的东西,但它不是一个函数或它没有该签名)。这取决于你如何处理——或者称之为程序员错误,让它使程序崩溃(对于一个Python开发人员来说,"应该"是明显的,是什么导致了那里的错误——一个未设置的duck接口),或者当你发现你的对象不支持你希望它做的事情时,捕捉并处理这些异常。实际上,在很多情况下,捕捉attributeError和typeError是很重要的。


抽象基类是一种深层次的魔力。我周期性地使用它们来实现一些东西,并对自己的聪明感到惊讶,不久之后我发现自己完全被自己的聪明所迷惑(尽管这很可能只是个人的局限)。

另一种方法(如果你问我的话,应该在pythonstdlibs中)是制作一个装饰器。

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
29
def abstractmethod(method):
   """
    An @abstractmethod member fn decorator.
    (put this in some library somewhere for reuse).

   """

    def default_abstract_method(*args, **kwargs):
        raise NotImplementedError('call to abstract method '
                                  + repr(method))
    default_abstract_method.__name__ = method.__name__    
    return default_abstract_method


class Shape(object):

    def __init__(self, shape_name):
       self.shape = shape_name

    @abstractmethod
    def foo(self):
        print"bar"
        return

class Rectangle(Shape):
    # note you don't need to do the constructor twice either
    pass  

r = Rectangle("x")
r.foo()

我没有写装修工。我刚想到会有人来。你可以在这里找到它:http://code.activestate.com/recipes/577666-abstract-method-decorator/good one jimmy2times。请注意该页底部的讨论,即装饰器的类型安全性。(如果有人倾斜,可以用检查模块固定)。


您可以使用six和abc高效地为python2和python3构建一个类,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import six
import abc

@six.add_metaclass(abc.ABCMeta)
class MyClass(object):
   """
    documentation
   """


    @abc.abstractmethod
    def initialize(self, para=None):
       """
        documentation
       """

        raise NotImplementedError

这是一份关于它的awesomoe文档。


必须实现抽象基类(abc)。

检查python文档