2011-06-18 3 views
13

Кажется, нет хорошей онлайн-документации по этому вопросу: Если я создаю производный класс, автоматически ли он будет иметь все атрибуты базового класса? Но для чего нужен BaseClass.__init(), вам также нужно сделать это для других методов базового класса? Нужно ли аргументы BaseClass.__init__()? Если у вас есть аргументы для вашего базового класса __init__(), они также используются производным классом, вам нужно явно указать аргументы на производный код classe __init__() или вместо них вместо BaseClass.__init__()?Атрибуты класса и базового класса на основе Python?

ответ

9

If I make a derived class, will it automatically have all the attributes of the base class?

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

Does BaseClass.init() need arguments?

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

class Base(object): 
    def __init__(self): 
     # something 

, то это довольно очевидно, что никакие другие аргументы не принимаются __init__. Если бы

class Base(object): 
    def __init__(self, argument): 
     # something 

, то вы должны пройти argument при вызове базы __init__. Здесь нет ракеты.

If you have arguments for your base class init(), are they also used by the derived class, do you need to explicitly set the arguments to the derived classe's init(), or set them to BaseClass.init() instead?

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

class Base(object): 
    def __init__(self, foo): 
     print 'Base' 

class Derived(Base): 
    pass 

Derived() # TypeError 
Derived(42) # prints Base 

В другом случае вам необходимо как-то позаботиться об этом. Используете ли вы *args, **kwargs и просто передаете аргументы без изменений в базовый класс или копируете подпись базового класса или поставляете аргументы из других источников, зависит от того, что вы пытаетесь выполнить.

43

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

class Foo(object): 
    def __init__(self): 
     self.a = 10 

    def do_something(self): 
     print self.a 

class Bar(Foo): 
    def __init__(self): 
     self.b = 20 

bar = Bar() 
bar.do_something() 

Это вызовет следующую ошибку:

AttributeError: 'Bar' object has no attribute 'a' 

Таким образом, метод do_something был унаследован, как и ожидалось, но этот метод требует атрибут a, был установлен, что он никогда не потому, что __init__ также была перезаписана. Мы оборачиваем это, явно называя Foo.__init__ с Bar.__init__.

class Foo(object): 
    def __init__(self): 
     self.a = 10 

    def do_something(self): 
     print self.a 

class Bar(Foo): 
    def __init__(self): 
     Foo.__init__(self) 
     self.b = 20 

bar = Bar() 
bar.do_something() 

который печатает 10 как и ожидалось.Foo.__init__ в этом случае ожидает единственный аргумент, который является экземпляром Foo (который по соглашению называется self).

Обычно, когда вы вызываете метод в экземпляре класса, экземпляр класса автоматически передается в качестве первого аргумента. Методы для экземпляра класса называются связанными методами. bar.do_something - пример связанного метода (и вы заметите, что он вызывается без каких-либо аргументов). Foo.__init__ - это несвязанный метод, поскольку он не привязан к конкретному экземпляру Foo, поэтому первый аргумент, экземпляр Foo, должен быть явно передан.

В нашем случае, мы переходим к selfFoo.__init__, который является экземпляром Bar, который был передан методу __init__ в Bar. Так как Bar наследует от Foo, экземпляры Bar также являются экземплярами Foo, поэтому разрешается прохождение self до Foo.__init__.

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

class Foo(object): 
    def __init__(self, a=10): 
     self.a = a 

    def do_something(self): 
     print self.a 

class Bar(Foo): 
    def __init__(self): 
     Foo.__init__(self, 20) 

bar = Bar() 
bar.do_something() 

, который будет печатать 20.

Если вы пытаетесь реализовать интерфейс, который полностью раскрывает все аргументы инициализации базового класса через ваш класс наследования, вам нужно будет сделать это явно. Обычно это делается с аргументами * args и ** kwargs (имена по соглашению), которые являются заполнителями для всех остальных аргументов, которые явно не указаны. Следующий пример использует все, что я обсуждал:

class Foo(object): 
    def __init__(self, a, b=10): 
     self.num = a * b 

    def do_something(self): 
     print self.num 

class Bar(Foo): 
    def __init__(self, c=20, *args, **kwargs): 
     Foo.__init__(self, *args, **kwargs) 
     self.c = c 

    def do_something(self): 
     Foo.do_something(self) 
     print self.c 


bar = Bar(40, a=15) 
bar.do_something() 

В этом случае аргумент c устанавливается равным 40, так как это первый аргумент Bar.__init__. Второй аргумент затем включается в переменные args и kwargs (* и ** - это особый синтаксис, который говорит о расширении списка/кортежа или словаря в отдельные аргументы при передаче функции/методу) и передается по Foo.__init__.

В этом примере также указывается, что любой перезаписанный метод должен быть вызван явно, если это то, что требуется (поскольку в этом случае do_something).

В последнем случае вы часто увидите super(ChildClass, self).method() (где ChildClass - это произвольный дочерний класс), который используется вместо вызова метода BaseClass явно. Обсуждение super - это еще один вопрос, но достаточно сказать, что в этих случаях его обычно используют для выполнения именно того, что делается, вызывая BaseClass.method(self). Вкратце, super делегирует вызов метода следующему классу в порядке разрешения метода - MRO (который в одиночном наследовании является родительским классом). См. documentation on super для получения дополнительной информации.

Смежные вопросы