Презаписване на метод на поле в 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]

Въпреки това, когато стартирам кода, получавам грешката „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. Вземете суровата функция 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