Как инициализировать базовый (супер) класс?

126

Считайте, что в Python у меня есть следующий код:

>>> class SuperClass(object):
    def __init__(self, x):
        self.x = x

>>> class SubClass(SuperClass):
    def __init__(self, y):
        self.y = y
        # how do I initialize the SuperClass __init__ here?

Как мне инициализировать SuperClass __init__в подклассе? Я следую руководству по Python, и оно не касается этого. Когда я искал в Google, я нашел несколько способов сделать. Каков стандартный способ справиться с этим?

Джереми
источник

Ответы:

147

Python (до версии 3) поддерживает классы «старого стиля» и «нового стиля». Классы нового стиля являются производными от того object, что вы используете, и вызывают их базовый класс super(), например

class X(object):
  def __init__(self, x):
    pass

  def doit(self, bar):
    pass

class Y(X):
  def __init__(self):
    super(Y, self).__init__(123)

  def doit(self, foo):
    return super(Y, self).doit(foo)

Поскольку python знает о классах старого и нового стиля, существуют разные способы вызова базового метода, поэтому вы нашли несколько способов сделать это.

Для полноты картины классы в старом стиле вызывают базовые методы явно с использованием базового класса, т.е.

def doit(self, foo):
  return X.doit(self, foo)

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

Python 3 знает только о классах нового стиля (независимо от того, унаследованы вы от них objectили нет).

Иво ван дер Вейк
источник
38

Обе

SuperClass.__init__(self, x)

или

super(SubClass,self).__init__( x )

будет работать (я предпочитаю второй, так как он больше соответствует принципу DRY).

См. Здесь: http://docs.python.org/reference/datamodel.html#basic-customization

adamk
источник
8
неправильно. super работает только с классами нового стиля и является единственным правильным способом вызова базы при использовании классов нового стиля. Кроме того, вам также необходимо явно передать self, используя конструкцию старого стиля.
Иво ван дер Вейк,
1
@Ivo - OP дал класс нового стиля в примере, и нет смысла говорить о разнице между новым и старым стилями, поскольку никто больше не должен использовать старый стиль. Ссылка, которую я дал (на документы Python), предполагает, что существует более одного «правильного» способа вызова суперкласса __init__.
adamk
21

Как инициализировать базовый (супер) класс?

class SuperClass(object):
    def __init__(self, x):
        self.x = x

class SubClass(SuperClass):
    def __init__(self, y):
        self.y = y

Используйте superобъект, чтобы убедиться, что вы получите следующий метод (как связанный метод) в порядке разрешения метода. В Python 2 вам нужно передать имя класса и selfsuper для поиска связанного __init__метода:

 class SubClass(SuperClass):
      def __init__(self, y):
          super(SubClass, self).__init__('x')
          self.y = y

В Python 3 есть небольшая магия, которая делает аргументы superненужными - и в качестве побочного преимущества это работает немного быстрее:

 class SubClass(SuperClass):
      def __init__(self, y):
          super().__init__('x')
          self.y = y

Жесткое кодирование родителя, как показано ниже, не позволяет вам использовать кооперативное множественное наследование:

 class SubClass(SuperClass):
      def __init__(self, y):
          SuperClass.__init__(self, 'x') # don't do this
          self.y = y

Обратите внимание, что __init__может быть только возвратNone - он предназначен для изменения объекта на месте.

Что-то __new__

Есть другой способ инициализировать экземпляры - и это единственный способ для подклассов неизменяемых типов в Python. Так что это необходимо , если вы хотите подкласс strили tupleили другой неизменный объект.

Вы можете подумать, что это метод класса, потому что он получает неявный аргумент класса. Но на самом деле это статический метод . Таким образом , вам нужно позвонить __new__с clsявно.

Обычно мы возвращаем экземпляр из __new__, поэтому, если вы это сделаете, вам также нужно вызвать свою базу __new__via superв своем базовом классе. Итак, если вы используете оба метода:

class SuperClass(object):
    def __new__(cls, x):
        return super(SuperClass, cls).__new__(cls)
    def __init__(self, x):
        self.x = x

class SubClass(object):
    def __new__(cls, y):
        return super(SubClass, cls).__new__(cls)

    def __init__(self, y):
        self.y = y
        super(SubClass, self).__init__('x')

Python 3 немного обошел стороной странности супервызовов, вызванных тем, __new__что он является статическим методом, но вам все равно нужно перейти clsк несвязанному __new__методу:

class SuperClass(object):
    def __new__(cls, x):
        return super().__new__(cls)
    def __init__(self, x):
        self.x = x

class SubClass(object):
    def __new__(cls, y):
        return super().__new__(cls)
    def __init__(self, y):
        self.y = y
        super().__init__('x')
Аарон Холл
источник