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

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

306142 次浏览

参见美国广播公司(abc)模块。基本上,你在类上定义__metaclass__ = abc.ABCMeta,然后用@abc.abstractmethod装饰每个抽象方法。除非重写了所有抽象方法,否则不能实例化从该类派生的类。

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

一个廉价的替代方法(也是在引入abc模块之前的最佳实践)是让所有抽象方法只引发一个异常(NotImplementedError是一个很好的异常),以便从它派生的类必须重写该方法才能有用。

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

沿着这些线,使用ABC

import abc


class Shape(object):
__metaclass__ = abc.ABCMeta
    

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

还可以阅读这个很好的教程:https://pymotw.com/3/abc/

你也可以查看,它是在python中引入ABC之前使用的。

你不能用语言原语。如上所述,美国广播公司(abc)包在Python 2.6及更高版本中提供了此功能,但Python 2.5及更早版本中没有此选项。abc包并不是Python的新特性;相反,它通过添加显式的“这个类是否说它这样做?”检查来添加功能,并使用手动实现的一致性检查,如果错误地声明此类声明,则会在初始化期间导致错误。

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

如果它在那里,那就太棒了,它就是有效的;这被称为duck typing,并且你的对象已经像鸭子一样嘎嘎叫了足够多来满足接口。由于基本方法需要特定的功能实现(泛型函数),因此为了强制重写,即使self是你调用该方法的对象工作得很好,因为self是一种约定,而不是任何实际特殊的东西。

异常出现在__init__中,因为当你的初始化式被调用时,派生类型的初始化式还没有被调用,所以它还没有机会将自己的方法钉钉到对象上。

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

在abc被引入之前,你会经常看到这种情况。

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




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

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

另一种方法(如果你问我,应该在python std库中)是创建一个装饰器。

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。请注意该页底部关于装饰器类型安全性的讨论。(如果有人愿意的话,可以用检查模块来修复)。

你可以使用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

这是一份很棒的文件。