Можно ли предотвратить вызов init? - PullRequest
6 голосов
/ 08 февраля 2012

Я редактирую оригинальный вопрос, потому что мы все фокусируемся на СЛЕДУЕТ, что вы когда-нибудь захотите это сделать.Мой вопрос просто МОЖЕТ ли я сделать это и КАК (понимая, что может быть несколько решений).Поэтому я просто собираюсь оставить фактический вопрос и вырезать фон.

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

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

Ответы [ 2 ]

10 голосов
/ 08 февраля 2012

Это вполне выполнимо, но я не думаю, что вы должны.Расскажите пользователям, как использовать ваш класс, и они должны подчиняться.Кроме того, если кто-то создает подклассы, он должен знать, как вызвать метод инициализации родителя.

В качестве доказательства концепции вот как это можно сделать с метаклассами (синтаксис Python 2.x):

>>> class WhoMovedMyInit(object):
        class __metaclass__(type):
            def __init__(self, *args, **kw):
                super(type,self).__init__(*args, **kw)
                if self.__init__ is not WhoMovedMyInit.__init__:
                    raise Exception('Dude, I told not to override my __init__')


>>> class IAmOk(WhoMovedMyInit):
        pass

>>> class Lol(WhoMovedMyInit):
        def __init__(self):
            pass

Traceback (most recent call last):
  File "<pyshell#35>", line 1, in <module>
    class Lol(WhoMovedMyInit):
  File "<pyshell#31>", line 6, in __init__
    raise Exception('Dude, I told not to override my __init__')
Exception: Dude, I told not to override my __init__

Вы также можете заменить метод подкласса __init__ на метод, который предупреждает пользователя или вызывает ошибку во время выполнения.

9 голосов
/ 08 февраля 2012

«Должен ли я это делать или нет, это отдельное обсуждение :)»

Пожалуйста, имейте это в виду.

Но это можно сделать - когда классРеализованный, не только синтаксис похож на вызов метода - с именем объекта класса после скобки - сам класс (который является объектом Python) называется - вызываемым объектом.

Вызов объектав Python вызывает магический метод __call__ в своем классе.Следовательно, при создании экземпляра класса вызывается метод __call__ для его метакласса.

То, что находится внутри этого метода __call__ в стандартном метаклассе (который является "типом"), примерно эквивалентно:

def __call__(cls, *args, **kw):
    self = cls.__new__(cls, *args, **kw)
    cls.__init__(self, *args, **kw)
    return self

Итак, если вы напишите метакласс, переопределяя __call__ и подавив в нем вызов __init__, он вообще не будет вызываться:

class Meta(type):
    def __call__(cls, *args, **kw):
        return cls.__new__(cls, *args, **kw)

class NoInit(object):
    __metaclass__ = Meta
    def __init__(self):
        print "Hello!"

NoInit()

Если вы хотите простово избежание того, что у подклассов есть __init__ вместо того, чтобы не вызывать его, вы можете создать гораздо более простой метакласс, который бы просто вызывал исключение во время создания класса:

class Meta(type):
    def __new__(metacls, name, bases, dct):
         if "__init__" in dct:
              raise NameError("Classes in this hierarchy should not have an __init__ method")
         return type.__new__(metacls, name, bases, dct)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...