Абстрактные методы в Python - PullRequest
20 голосов
/ 02 мая 2011

Мне нужно что-то вроде abstract protected метода в 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?

Является ли хорошим стилем использование подчеркивания для абстрактных методов, которое будет protected в других языках?

Улучшит ли абстрактный базовый класс (abc) что-нибудь?

Ответы [ 3 ]

37 голосов
/ 02 мая 2011

В 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() обычно избегают в пользу типизации утки. Используйте его, если вам это нужно, но не как общий шаблон для определения интерфейсов.

5 голосов
/ 02 мая 2011

В случае сомнений поступайте так, как поступает Гвидо.

Подчеркивания нет.Просто определите «абстрактный метод» как однострочник, который вызывает NotImplementedError:

class Abstract():
    def ConcreteMethod(self):
        raise NotImplementedError("error message")
0 голосов
/ 02 мая 2011

По сути, пустой метод в базовом классе здесь не нужен.Просто сделайте это так:

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 необходимо реализовать в подклассах.

...