Почему методы инициализации суперкласса не вызываются автоматически?


почему дизайнеры Python решили, что подклассы'__init__() методы не вызывают автоматически __init__() методы их суперклассов, как в некоторых других языках? Это обновления и рекомендуемые фразеологизм очень нравится следующий?

class Superclass(object):
    def __init__(self):
        print 'Do something'

class Subclass(Superclass):
    def __init__(self):
        super(Subclass, self).__init__()
        print 'Do something else'
9 127

9 ответов:

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

в принципе, делегирование инициализатора суперкласса не является автоматическим в Python по тем же причинам, по которым такое делегирование также не является автоматическим для любой другие методы -- и обратите внимание, что эти "другие языки" не выполняют автоматическое делегирование суперкласса для любого другого метода... просто для конструктора (и если применимо, деструктора), который, как я уже упоминал, является не что в Python __init__ есть. (Поведение __new__ также довольно своеобразно, хотя на самом деле не имеет прямого отношения к вашему вопросу, так как __new__ Это такой своеобразный конструктор, что на самом деле не обязательно нужно строить что угодно-вполне может вернуть существующий экземпляр или даже не экземпляр... ясно, что Python предлагает вам много больше контроля над механикой, чем "другие языки", которые вы имеете в виду, которые и включает в себя отсутствие автоматического делегирования в !-).

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

причина проста: вы не обязательно строите производный класс таким же образом, как вы строите базовый класс. У вас может быть больше параметров, меньше, они могут быть в другом порядке, или не связаны вообще.

class myFile(object):
    def __init__(self, filename, mode):
        self.f = open(filename, mode)
class readFile(myFile):
    def __init__(self, filename):
        super(readFile, self).__init__(filename, "r")
class tempFile(myFile):
    def __init__(self, mode):
        super(tempFile, self).__init__("/tmp/file", mode)
class wordsFile(myFile):
    def __init__(self, language):
        super(wordsFile, self).__init__("/usr/share/dict/%s" % language, "r")

это относится ко всем производным методам, а не только __init__.

Java и C++ требуются что конструктор базового класса вызывается из-за разметки памяти.

если у вас есть класс BaseClass с , а вы создаете новый класс SubClass добавляет , то экземпляр SubClass содержит место для field1 и field2. Вам нужен конструктор BaseClass заполнить field1, если не требуется, чтобы все наследующие классы повторяли BaseClassинициализация в собственных конструкторах. И если field1 является частным, а затем наследует классы не могу инициализации field1.

Python-это не Java или C++. Все экземпляры всех пользовательских классов имеют одинаковую "форму". Это в основном просто словари, в которые можно вставить атрибуты. Перед любой инициализацией все экземпляры всех пользовательских классов почти точно так же; это просто места для хранения атрибутов, которые еще не хранят их.

так это имеет смысл для подкласса Python не вызывать его конструктор базового класса. Он мог бы просто добавить атрибуты сам, если бы захотел. Нет места, отведенные для определенного количества месторождений для каждого класса в иерархии, и нет никакой разницы между атрибутом добавил код BaseClass метод и атрибут добавил код SubClass метод.

если, как обычно,SubClass на самом деле хочет иметь все BaseClassинварианты настраиваются до того, как это будет сделано своя настройка, то да вы можете просто позвонить BaseClass.__init__() (или использовать super, но это сложно и иногда имеет свои проблемы). Но тебе и не нужно. И вы можете сделать это до, или после, или с разными аргументами. Черт, если бы вы хотели, вы могли бы позвонить BaseClass.__init__ из другого метода полностью, чем __init__; может быть, у вас есть какая-то странная ленивая инициализация.

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

"явное лучше, чем неявное."Это то же самое рассуждение, которое указывает на то, что мы должны явно писать "я".

Я думаю, что в конце концов это преимущество - можете ли вы прочитать все правила Java относительно вызова конструкторов суперклассов?

часто подкласс имеет дополнительные параметры, которые не могут быть переданы на суперкласс.

сейчас у нас есть довольно длинная страница, описывающая порядок разрешения метода в случае множественного наследования:http://www.python.org/download/releases/2.3/mro/

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

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

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

Я считаю, что одно очень важное соображение здесь заключается в том, что с автоматическим вызовом super.__init__(), вы запрещаете, по дизайну, когда этот метод инициализации вызывается, и с какими аргументами. отказ от автоматического вызова и требование от программиста явно выполнить этот вызов влечет за собой большую гибкость.

в конце концов, только потому, что класс B является производным от класса A не означает A.__init__() может или должен вызываться с теми же аргументами, что и B.__init__(). сделать звонок явное означает, что программист может иметь, например, define B.__init__() С совершенно другими параметрами, сделайте некоторые вычисления с этими данными, вызовите A.__init__() с аргументами, подходящими для этого метода, а затем сделать некоторые постобработки. такого рода гибкость было бы неудобно достичь, если A.__init__() будет вызван из B.__init__() неявно, либо перед B.__init__() выполняет или сразу после него.

чтобы избежать путаницы, полезно знать, что вы можете вызвать класса __init__() метод, если child_class не имеет __init__() класса.

пример:

class parent:
  def __init__(self, a=1, b=0):
    self.a = a
    self.b = b

class child(parent):
  def me(self):
    pass

p = child(5, 4)
q = child(7)
z= child()

print p.a # prints 5
print q.b # prints 0
print z.a # prints 1

на самом деле MRO в python будет искать __init__() в родительском классе, когда не может найти его в классе детей. Вы должны вызвать конструктор родительского класса, если у вас уже есть __init__() метод в классе children.

например, следующий код будет возвращать ошибку: родительский класс : def init(self, a=1, b=0): личность.ля личность.b = b

    class child(parent):
      def __init__(self):
        pass
      def me(self):
        pass

    p = child(5, 4) # Error: constructor gets one argument 3 is provided.
    q = child(7)  # Error: constructor gets one argument 2 is provided.

    z= child()
    print z.a # Error: No attribute named as a can be found.