Нерушимый суперкласс

Итак, я пишу модуль для подключения к внешним поставщикам счетов (Twitter, Facebook и т.д.), и у меня есть суперкласс, который бесполезен сам по себе, но содержит общие методы, которые должны быть вызваны подклассами для сохранения аутентификации токенов, получения токенов авторизации и деавторизации провайдера. Мой вопрос в том, есть ли способ сделать его нежизнеспособным или я должен следовать за соглашающимся взрослым правилом и позволить всем, кто его использует, делать ошибки по своему усмотрению? Помимо docstring есть хороший способ обозначить, что кто-то не должен использовать этот суперкласс самостоятельно?

Ответы

Ответ 1

Я подражаю Sven Marnach edit: Я думаю, вы должны следовать правилу "соглашающиеся взрослые" и упоминать в docstring, что класс не предназначен для создания экземпляра,

Ключевая фраза в вашем вопросе: "У меня есть суперкласс, который бесполезен сам по себе". Он не будет ссылаться на cthulhu при создании экземпляра; это не приведет к какому-то катастрофическому, трудно отладочному отказу где-то еще в вашей программе; это будет просто небольшая трата времени. Думаю, что это не стоит калечить класс.

Ответ 2

class NoInstantiation:    # "class NoInstantiation(object):" in Python 2.2+ or whatever
    def __new__(cls):
        "This class is not meant to be instantiated, so __new__ returns None."
        return None

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

EDIT: если вы действительно хотите быть приятным, вы также можете распечатать предупреждение stderr в __new__() об этом или даже выбросить исключение.

РЕДАКТИРОВАТЬ 2: Если вы отправите маршрут исключения, возможно, вы захотите поднять исключение в методе __init__() суперкласса, так как пользователи все равно будут переопределять __init__() в своих подклассах.

ИЗМЕНИТЬ 3: Также существует возможность установки __new__ или __init__ равным None, хотя результирующая ошибка не будет очень информативной.

Ответ 3

Основываясь на ответе JAB, было бы удобнее написать __new__() следующим образом:

class NoInstantiation(object):
    def __new__(cls, *args, **kwargs):
        if cls is NoInstantiation:
            raise RuntimeError(
                "NoInstantiation isn't meant to be instantiated")
        else:
            return super(NoInstantiation, cls).__new__(cls, *args, **kwargs)

Таким образом, вам не нужно перезаписывать __new__() в производных классах.

Изменить. Я опубликовал этот ответ как улучшение ответа JAB, но я бы рекомендовал не использовать этот код. Это как-то преднамеренно калечит ваш класс. Обычный способ Python состоит в том, чтобы четко документировать класс, который не предназначен для установки. Но, может быть, кто-то найдет способ, как это все равно, - вы никогда не знаете, каким образом люди будут использовать вашу библиотеку.

Ответ 4

Вы можете использовать abstractmethod decorator в модуле abc, чтобы отметить один из методов, которые все производные классы переопределяют как аннотация.

In [1]: import abc

In [2]: class C:
   ...:     __metaclass__ = abc.ABCMeta
   ...:     
   ...:     @abc.abstractmethod
   ...:     def my_abstract_method(self, *args) :
   ...:         pass
   ...:     
   ...:     

In [3]: c = C()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

/home/afoglia/<ipython console> in <module>()

TypeError: Can't instantiate abstract class C with abstract methods my_abstract_method

Я действительно сомневаюсь в организации класса. Если этот "суперкласс" - это не что иное, как функции, которые должны быть вызваны подклассами, почему это не просто модуль, который может использовать другой механизм? Если это определение интерфейса, которое производные классы полностью реализуют (возможно, как шаблон метода шаблона), то абстрактные методы представляют собой функции, которым должны быть предоставлены реализации производным классом. В этом случае я даже не стал бы делать базу неконструктивной, и либо люди получили бы необходимую информацию, полученную с помощью метода factory, либо позволяли бы пользователю понять это, когда он вызывает функцию, которая терпит неудачу. (Дайте комментарий в docstrings, хотя.)