Подтвердить что ты не робот

Абстрактные классы Python - как препятствовать созданию экземпляров?

Я исхожу из фона С#, где язык имеет встроенные функции "защитить разработчика". Я понимаю, что Python использует подход "мы все взрослые здесь" и возлагаем ответственность на разработчика задумчиво и тщательно.

Тем не менее, Python предлагает условные обозначения, например, подчеркивание для частных переменных экземпляра. Мой вопрос в том, что существует конкретное соглашение для маркировки класса как абстрактного, отличного от простого указания его в docstrings? Я ничего не видел в руководстве по стилю python, в котором упоминаются соглашения об именах для абстрактных классы.

Я могу представить 3 варианта, но я не уверен, что они хорошие идеи:

  • Укажите его в docstring над классом (может быть упущено)
  • Используйте главное подчеркивание в имени класса (не уверен, что это общепринято)
  • Создайте метод def __init__(self): для абстрактного класса, который вызывает ошибку (не уверен, что это отрицательно влияет на наследование, например, если вы хотите вызвать базовый конструктор)

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

4b9b3361

Ответ 1

Если вы используете Python 2.6 или выше, вы можете использовать модуль Абстрактный базовый класс из стандартной библиотеки, если вы хотите применить абстрактность, Вот пример:

from abc import ABCMeta, abstractmethod

class SomeAbstractClass(object):
    __metaclass__ = ABCMeta

    @abstractmethod
    def this_method_must_be_overridden(self):
        return "But it can have an implementation (callable via super)."

class ConcreteSubclass(SomeAbstractClass):
    def this_method_must_be_overridden(self):
        s = super(ConcreteSubclass, self).this_method_must_be_overridden()
        return s.replace("can", "does").replace(" (callable via super)", "")

Вывод:

>>> a = SomeAbstractClass()
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    a = SomeAbstractClass()
TypeError: Can't instantiate abstract class SomeAbstractClass with abstract
methods this_method_must_be_overridden
>>> c = ConcreteSubclass()
>>> c.this_method_must_be_overridden()
'But it does have an implementation.'

Ответ 2

Основываясь на вашем последнем предложении, я бы ответил на ответ "просто документируйте его". Любой, кто использует класс так, как указано в документации, не должен принимать на себя ответственность за какое-либо странное поведение.

В Python существует механизм абстрактного базового класса, но я не вижу причин использовать его, если ваша единственная цель - препятствовать созданию экземпляра.

Ответ 3

Создайте свой "абстрактный" класс и raise NotImplementedError() в абстрактных методах.

Это не остановит людей, использующих этот класс, и, по-настоящему печатая на уток, даст вам знать, если вы пренебрегаете реализацией абстрактного метода.

Ответ 4

Я просто называю свои абстрактные классы префиксом "Аннотация". Например. AbstractDevice, AbstractPacket и т.д.

Это примерно так же легко и точно, как оно есть. Если другие предпочитают идти вперед и создавать экземпляр и/или использовать класс, начинающийся со слова "Аннотация", то они либо знают, что делают, либо не надеются на них в любом случае.

Именовать его таким образом, также служит напоминанием о себе, чтобы я не сходил с ума с иерархиями глубокой абстракции, потому что постановка "Реферат" на переднем плане многих классов тоже глупо.

Ответ 5

Обеспечение исполнения вещей возможно, но не бессовестное. Когда я пришел на Python после многих лет программирования на С++, я также попытался сделать то же самое, я полагаю, большинство людей пытаются это сделать, если у них есть опыт работы на более классических языках. Метаклассы выполняли бы эту работу, но в любом случае Python проверяет очень мало вещей во время компиляции. Проверка будет выполняться во время выполнения. Итак, неспособность создать определенный класс, действительно полезный, если он обнаружен только во время выполнения? В С++ (и в С# тоже) вы даже не можете скомпилировать код, создающий абстрактный класс, и в этом весь смысл - как можно скорее обнаружить проблему. Если у вас есть абстрактные методы, повышение исключения NotImplementedError кажется вполне достаточным. NB: повышение, не возвращающее код ошибки! В Python ошибки обычно не должны быть тихими, если thay явно не затенены. Документирование. Именование класса таким образом, чтобы он был абстрактным. Это все.

Качество кода Python обеспечивается в основном методами, которые сильно отличаются от методов, используемых на языках с расширенной проверкой типа компиляции. Лично я считаю, что самое серьезное различие между динамически типизированными lngauges и другими. Единичные тесты, анализ охвата и т.д. В результате дизайн кода совсем другой: все делается не для обеспечения соблюдения правил, а для того, чтобы сделать их максимально легкими.