Python: как наследовать и переопределять - PullRequest
14 голосов
/ 16 мая 2010

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

Я получаю объект типа 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__?

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

Ответы [ 3 ]

11 голосов
/ 16 мая 2010

То, как вы строите объект подкласса 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__ считается «состоянием» экземпляра). Он может возвращать dict (в этом случае вызов .update обновляет состояние self), но он также может возвращать что-либо еще, если класс также определяет __setstate__, который его принимает (поэтому этот код сначала пытается выполнить этот маршрут , прежде чем вернуться к возможности обновления). Обратите внимание, что в этом случае один или оба специальных метода были бы унаследованы от A - я бы не определил / не переопределил их в B (если, конечно, таким способом не будут достигнуты другие тонкие цели; - ).

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

5 голосов
/ 16 мая 2010

Попробуйте это:

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
1 голос
/ 16 мая 2010

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

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

...