我在使用Python继承时遇到了麻烦。虽然这个概念在Java中对我来说似乎太容易了,但到目前为止我一直无法理解Python,这至少让我感到惊讶。
我有一个原型如下:
class Shape():
def __init__(self, shape_name):
self.shape = shape_name
class Rectangle(Shape):
def __init__(self, name):
self.shape = name
在上面的代码中,我如何制作一个需要为所有子类实现的抽象方法?
答案 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)。