Python中的抽象方法

24

我需要类似于Python(3.2)中的抽象保护方法:

class Abstract:
    def use_concrete_implementation(self):
        print(self._concrete_method())

    def _concrete_method(self):
        raise NotImplementedError()


class Concrete(Abstract):
    def _concrete_method(self):
        return 2 * 3

定义一个"抽象"方法只为了引发NotImplementedError,这真的有用吗?

在Python中,使用下划线来表示抽象方法,这是良好的编程风格吗?在其他语言中,这些方法通常被视为受保护的。

使用抽象基类(abc)是否会改善任何问题?

3个回答

43

在Python中,通常可以避免使用这样的抽象方法。你通过文档定义一个接口,并假设传入的对象符合该接口(“鸭子类型”)。

如果您真的想定义一个带有抽象方法的抽象基类,可以使用abc模块实现:

from abc import ABCMeta, abstractmethod

class Abstract(metaclass=ABCMeta):
    def use_concrete_implementation(self):
        print(self._concrete_method())

    @abstractmethod
    def _concrete_method(self):
        pass

class Concrete(Abstract):
    def _concrete_method(self):
        return 2 * 3

请注意,这不是通常的Python编程方式。 abc模块的主要目标之一是引入一种重载isinstance()的机制,但通常应该避免使用isinstance()检查,而是优先采用鸭子类型。如果需要,可以使用它,但不要将其作为定义接口的一般模式。


1
这个观点很好,一开始就不是一个“Pythonic问题”。 - Kenan Banks
1
但是为什么不在代码本身中使用“文档”,并在“抽象”方法的主体中引发NotImplementedError呢? - deamon
1
@deamon:如果你简单地省略了这个方法,你会得到一个“AttributeError”。如果你实现它并引发“NotImplementedError”,那么你将得到后者的异常。如果你觉得不同的错误消息值得额外的代码,那就去做吧 :) 我经常认为“AttributeError”已经足够清晰明了,但在某些情况下,你可能希望事情更加明确。 - Sven Marnach
3
class Abstract(metaclass=ABCMeta) 只能在 Python 3 中使用,是吗? - clstaudt

10

当你不确定时,像Guido一样做。

不要使用下划线。只需将“抽象方法”定义为一行代码,该代码会引发NotImplementedError:

class Abstract():
    def ConcreteMethod(self):
        raise NotImplementedError("error message")

3
但是Guido在抽象“protected”方法中使用下划线;-) - deamon
14
Guido从不在方法名中使用驼峰命名法 :) - Sven Marnach
5
为什么您会把一个抽象方法命名为“Conrete”(具体的)? - Ethan Furman
2
@Ethan Furman:因为程序员需要实现该名称的方法。抽象类必须始终使用稍后要使用的名称命名抽象方法。 - pepr
1
@pepr:哦,对了——我问那个问题的时候能不能声称很累?可能是花园疲劳引起的吧?;) - Ethan Furman
1
Google Codesearch貌似已经不存在了,链接现在已经失效。 - Martijn Courteaux

0

基本上,在基类中创建一个空方法在这里是不必要的。只需要像这样做:

class Abstract:
    def use_concrete_implementation(self):
        print(self._concrete_method())

class Concrete(Abstract):
    def _concrete_method(self):
        return 2 * 3

事实上,在Python中通常甚至不需要基类。由于所有调用都是动态解析的,如果方法存在,则会被调用,否则将引发AttributeError
注意:文档中需要提到_concrete_method需要在子类中实现。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接