1

I have a custom class which I instantiate twice in a second class:

[EDIT: The old code wasn't really capturing what my issue is, I'm rewriting to make it clearer. Foo is going to process network events (and I need multiple connections, hence why Bar will have multiple Foo fields and can't just inherit) by using a callback function. In the Bar class, i want to overwrite the callback to do something different. Rewriting callb in Foo won't work, because I do inherit from it in other instances]

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()

[When run() is called in a Foo, it works correctly. However, I cannot overwrite the callb method of Foo from Bar - the self._x which should be referred to by Foo is trying to call from Bar]

However, when running the code, I receive the error "Bar has no attribute '_x'" rather than my expected value of 50. Obviously, the self in Bar's test is still referring to Bar, not the Foo which is actually calling the method. I would have expected it to be the internal Foo self._foo field in Bar.

I suspect my problem has something to do with name mangling, but I haven't been able to find a descriptive source when I have fields on self having their methods overwritten. Most examples seem to be when I'm inheriting from a different class.

user2093082
  • 407
  • 8
  • 19

3 Answers3

1

it seems that in Bar.test you want :

print self._foo._x * 10
dugres
  • 12,613
  • 8
  • 46
  • 51
1

Ok short lesson on how classes in Python work: Any function stored on a class object (a "method") is automatically fetched by a descriptor (which describes how to getl attributes from objects) that binds the methods to the class or instance you used to get it.

For example:

Foo.test 

is a unbound method of class Foo: This is a object created by the descriptor on Foo that knows it was fetched from a class object

Foo().test 

is a bound method of a instance of class Foo: This method stores the instance of Foo used to fetch the function. This instance is automatically passed as the first argument when you call the resulting object and that's why you write self as the first parameter when you define a method.

So, what you are trying to do is this:

  1. Get the raw function test from class Bar (not through a descriptor)
  2. Bind this function to a instance of Foo
  3. Store this function on the foo instance

So, this is how you do it:

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

See this question for more details on descriptors.

Community
  • 1
  • 1
Jochen Ritzel
  • 104,512
  • 31
  • 200
  • 194
  • Awesome - this is exactly what I was after. I'll do a little more testing to confirm but I'm preemptively marking as correct based on the first one. – user2093082 Apr 08 '13 at 22:59
  • While this is true, and a neat hack, functions are first class objects, so why not just pass a value and a function into Foo in the first place? – Aaron McMillin Oct 03 '13 at 14:22
0

If you look at your code, when you call b.test, you are calling the method directly on an instance of Bar; no instance of Foo is mentioned at all. b doesn't have a member named _x, hence the error. To get the behaviour you expect, you must call b._foo.test. This will give you your expected result of 50.

If you want to be able to call b.test directly, change self._x to self._foo._x. This way you don't have to change any methods of _foo. I'm not really sure why you would assign different methods to an object when you could use inheritance anyway.

Matt Randell
  • 374
  • 1
  • 3
  • 11