Python中的抽象方法

时间:2010-12-07 23:58:20

标签: python

我在使用Python继承时遇到了麻烦。虽然这个概念在Java中对我来说似乎太容易了,但到目前为止我一直无法理解Python,这至少让我感到惊讶。

我有一个原型如下:

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

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

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

7 个答案:

答案 0 :(得分:273)

在介绍abc之前,你会经常看到这个。

class Base(object):
    def go(self):
        raise NotImplementedError("Please Implement this method")


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

答案 1 :(得分:227)

沿着这些方向的东西,使用ABC

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/

您还可以查看在python中引入ABC之前使用的 zope.interface

答案 2 :(得分:42)

请参阅abc module。基本上,您在类上定义__metaclass__ = abc.ABCMeta,然后用@abc.abstractmethod装饰每个抽象方法。除非已覆盖所有抽象方法,否则无法实例化从此类派生的类。

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

一个廉价的替代方案(以及引入abc模块之前的最佳实践)将使所有抽象方法只引发异常(NotImplementedError是一个很好的例子),以便从它必须覆盖该方法才有用。

然而,abc解决方案更好,因为它使这些类完全不被实例化(即,它“更快地失败”),并且还因为你可以提供每个方法的默认或基本实现使用派生类中的super()函数来访问。

答案 3 :(得分:7)

你不能,用语言原语。正如已经提到的那样,abc package在Python 2.6及更高版本中提供了这一功能,但是Python 2.5及更早版本没有选项。 abc包不是Python的新功能;相反,它通过添加显式添加功能“这个类说这样做吗?”检查,手动实现的一致性检查,如果错误地进行了这样的声明,则会在初始化期间导致错误。

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

如果它在那里,太棒了,它只是起作用;这叫做duck typing,你的对象就像鸭子一样嘎嘎叫来满足界面。即使self是您在上调用此类方法的对象,这也可以正常工作,因为需要特定功能实现的基本方法(通用函数)导致强制覆盖,因为self是惯例,而不是任何特别的。

异常在__init__中,因为在调用初始化程序时,派生类型的初始化程序没有,因此它还没有机会将自己的方法装订到对象上。

如果没有实现该方法,你将得到一个AttributeError(如果它根本不存在)或TypeError(如果有那个名称的东西,但它不是一个函数或者它没有那个签名) 。这取决于你如何处理它 - 要么称它为程序员错误并让它崩溃程序(对于python开发人员来说它应该是显而易见的,这会导致那种错误 - 一个未满足的duck接口),或者捕获和处理当您发现您的对象不支持您希望它执行的操作时,这些异常。实际上,捕获AttributeError和TypeError在很多情况下都很重要。

答案 4 :(得分:6)

抽象基类是深刻的魔力。我经常使用它们实现一些东西,并且对自己的聪明感到惊讶,不久之后我发现自己完全被自己的聪明所迷惑(尽管这可能只是个人限制)。

这样做的另一种方式(如果你问我应该在python std libs中)就是做装饰。

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/好一个jimmy2times。请注意该页面底部的讨论。类型装饰的安全性。 (如果有人这么倾向,可以通过检查模块修复)。

答案 5 :(得分:4)

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

import six
import abc

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

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

This是一个awesomoe文件。

答案 6 :(得分:3)

您必须实现一个抽象基类(ABC)。

Check python docs