Python: как унаследовать и переопределить

Рассмотрим эту ситуацию:

Я получаю объект типа A, который имеет функцию f. То есть:

class A:
   def f(self):
      print 'in f'
   def h(self):
      print 'in h'

и я получаю экземпляр этого класса, но я хочу переопределить функцию f, но сохранить остальные функции A. Так что я думал, было что-то вроде:

class B(A):
     def __init__(self, a):
        #something here
     ....

     def f(self):
         print 'in B->f'

и использование будет:

def main(a):
   b = B(a)
   b.f()   #prints "in B->f"
   b.h()   #print "in h"

Что я хочу - это своего рода конструктор копирования, который получает родительский элемент текущего класса (A) и возвращает экземпляр этого класса (B).

Как вы это делаете? Как выглядит метод __init__?

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

Ответы

Ответ 1

Как вы строите объект подкласса B "на основе" одного из классов A зависит исключительно от того, как последнее сохраняет состояние, если оно есть, и как вам лучше всего добраться до этого состояния и скопировать его. В вашем примере экземпляры A не имеют состояния, поэтому в B '__init__' нет необходимости работать. В более типичном примере скажем:

class A(object):
   def __init__(self):
     self._x = 23
     self._y = 45
   def f(self):
      print 'in f,', self._x
   def h(self):
      print 'in h,', self._y

состояние будет находиться в двух атрибутах экземпляра _x и _y, поэтому это то, что вам нужно скопировать:

class B(A):
     def __init__(self, a):
        self._x = a._x
        self._y = a._y

     def f(self):
         print 'in B->f,', self._x

Это самый распространенный и нормальный подход, когда подкласс принимает и непосредственно реализует свою зависимость от состояния над суперклассом - он очень прямолинейный и линейный.

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

Можно добавить небольшое прикосновение к "магии" (программирование на основе интроспекции), например...:

class B1(A):
    def __init__(self, a):
        try: s = a.__getstate__()
        except AttributeError: s = a.__dict__
        try: self.__setstate__(s)
        except AttributeError: self.__dict__.update(s)

getstate - это специальный метод, который могут определять классы - если они это делают (например, путем травления), чтобы "получить состояние" своих экземпляров для целей сериализации (в противном случае экземпляр __dict__ считается экземпляром "state" ). Он может вернуть dict (в этом случае вызов .update обновляет состояние self), но он может также вернуть что-либо еще, если класс также определяет __setstate__, который его принимает (так что этот код сначала пытается этот маршрут, прежде возврат к возможности обновления). Обратите внимание, что в этом случае использование одного или обоих специальных методов будет унаследовано от A - я бы не определял/не переопределял их в B (если только не будут достигнуты такие тонкие цели, которые, конечно,).

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

Ответ 2

Попробуйте следующее:

class A:
  def f(self):
    print("in f")

  def h(self):
    print("in h")

class B(A):
  def f(self):
    print("in B:f")

def test(x):
  x.f()
  x.h()

test(A())
test(B())

Заметьте, я использую Python 3, что является причиной для print принятия аргументов в скобках.

Вывод:

in f
in h
in B:f
in h

Ответ 3

Вам нужно поместить аргумент self в список аргументов для методов экземпляра в python.

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