Почему методы инициализации суперкласса не вызываются автоматически?
почему дизайнеры 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 ответов:
решающее различие между питоном
__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__()
. сделать звонок явное означает, что программист может иметь, например, defineB.__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.