Перезапись метода поля в Python

У меня есть собственный класс, который я дважды создаю во втором классе:

[РЕДАКТИРОВАТЬ: старый код на самом деле не отражал мою проблему, я переписываю, чтобы было понятнее. Foo будет обрабатывать сетевые события (а мне нужно несколько подключений, поэтому Bar будет иметь несколько полей Foo и не может просто наследовать) с помощью функции обратного вызова. В классе Bar я хочу перезаписать обратный вызов, чтобы сделать что-то другое. Переписать callb в Foo не получится, потому что я наследую от него в других экземплярах]

class Foo(object):
    def __init__(self, x):
        self._x = x
        #When an event happens, call callb()
        self.on_certain_event(callback=callb)

    def callb(self):
        print self._x

    def run(self):
        waitForEventsInfinitely() #

class Bar(object):
    def __init__(self):
        self._foo = Foo(5)
        #OVERWRITE THE EXISTING CALLBACK METHOD IN THE Foo CLASS!
        self._foo.callb = self.callb

    def callb(self):
        print self._x * 10

    def run(self):
        waitForEventsInfinitely() # Not going to actually write t

f = Foo(2)
f.run()
b = Bar()
b.run()

[Когда run () вызывается в Foo, он работает правильно. Однако я не могу перезаписать метод callb Foo из Bar - self._x, на который должен ссылаться Foo, пытается вызвать из Bar]

Однако при запуске кода я получаю ошибку «Бар не имеет атрибута '_x'», а не мое ожидаемое значение 50. Очевидно, что self в тесте Bar по-прежнему ссылается на Bar, а не на Foo, который фактически вызывает метод . Я ожидал, что это будет внутреннее поле Foo self._foo в Bar.

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


person user2093082    schedule 08.04.2013    source источник
comment
Думаю, тебе может понадобиться print self._foo._x * 10   -  person Wessie    schedule 09.04.2013
comment
Если вы перезаписываете методы, почему бы вам просто не унаследовать Foo от Bar? Похоже, вы пытаетесь имитировать наследование - почему бы просто не использовать наследование?   -  person cdhowie    schedule 09.04.2013
comment
Это __init или __init__?   -  person Xymostech    schedule 09.04.2013
comment
@cdhowie Потому что в моем реальном коде у меня будет несколько экземпляров Foo в одной строке   -  person user2093082    schedule 09.04.2013


Ответы (3)


Хорошо, короткий урок о том, как работают классы в Python: любая функция, хранящаяся в объекте класса («метод»), автоматически выбирается дескриптор (который описывает, как получить атрибуты из объектов), который связывает методы с классом или экземпляром, который вы использовали для его получения.

Например:

Foo.test 

является несвязанным методом класса Foo: это объект, созданный дескриптором на Foo, который знает, что он был получен из объекта класса

Foo().test 

является связанным методом экземпляра класса Foo: этот метод хранит экземпляр Foo, используемый для выборки функции. Этот экземпляр автоматически передается в качестве первого аргумента при вызове результирующего объекта, и поэтому вы пишете self в качестве первого параметра при определении метода.

Итак, вы пытаетесь сделать следующее:

  1. Получить raw функцию test из класса Bar (не через дескриптор)
  2. Свяжите эту функцию с экземпляром Foo
  3. Сохраните эту функцию в экземпляре foo

Итак, вот как вы это делаете:

import types 

class Bar(object):
    def __init__(self):
        self._foo = Foo(5)

        raw_function = Bar.test_Bar.__func__                        # 1)
        bound_to_foo = types.MethodType(raw_function, self._foo)    # 2)    
        self._foo.test = bound_to_foo                               # 3)

    def test(self):
        print self._x * 10

См. этот вопрос для получения дополнительных сведений о дескрипторах.

person Jochen Ritzel    schedule 08.04.2013
comment
Потрясающе - это именно то, что я искал. Я проведу еще немного тестов, чтобы подтвердить, но я заранее отмечаю правильность на основании первого. - person user2093082; 09.04.2013
comment
Хотя это правда, и это изящный прием, функции являются объектами первого класса, так почему бы просто не передать значение и функцию в Foo в первую очередь? - person Aaron McMillin; 03.10.2013

похоже, что в Bar.test вы хотите:

print self._foo._x * 10
person dugres    schedule 08.04.2013

Если вы посмотрите на свой код, когда вы вызываете b.test, вы вызываете метод непосредственно на экземпляре Bar; ни один экземпляр Foo не упоминается вообще. b не имеет члена с именем _x, отсюда и ошибка. Чтобы получить ожидаемое поведение, вы должны вызвать b._foo.test. Это даст вам ожидаемый результат 50.

Если вы хотите иметь возможность напрямую вызывать b.test, измените self._x на self._foo._x. Таким образом, вам не нужно изменять какие-либо методы _foo. Я не совсем уверен, зачем вы назначаете объекту разные методы, если в любом случае можно использовать наследование.

person Matt Randell    schedule 08.04.2013