episodeyang
episodeyang

Reputation: 782

self reference in monkey patched python class

I am trying to understand the self reference in a patched method. The code is the following:

class foo():
    def __init__(self):
        self.child = lambda: none
        self.child.string = "child's string value"
        self.string = "string value 1"
        print "initialized"
    def func1(self):
        print self.string

if __name__=="__main__":
    foo0=foo()
    foo0.newMethod = lambda: "test"
    print foo0.newMethod()

    foo0.func1()    
    foo0.child.secondMethod = foo0.func1
    foo0.child.secondMethod()

Why does the child's method refer its self to foo0? Is this any different from javascript?

Upvotes: 0

Views: 92

Answers (1)

Max Noel
Max Noel

Reputation: 8910

The answer to that question lies in Python's distinction between unbound method and bound methods. When you define a method, it's unbound: it can operate on any instance of the class, and it is, in fact, nothing more than a function whose first argument is named self (and even then, it's a convention) and a little bit of checking so you don't do truly horrible things with them.

class Foo(object):
    def my_method(self, arg):
        print "self:", self
        print "arg:", arg

a = Foo()
print Foo.my_method(a, 2)
# self: self: <__main__.Foo object at 0x000000000219DE10>
# arg: 2

# You could, in theory, do that, but Python complains because it values the sanity
# of the people who will have to maintain your code :p
print Foo.my_method(1, 2)
# TypeError: unbound method my_method() must be called with Foo instance as first argument (got int instance instead)

So far, nothing special. However, Python has a bit of magic when we call a method not on the class itself (as is done above), but on an instance of that class (that is, on an object).

a.my_method(2)
# self: <__main__.Foo object at 0x000000000219DE10>
# arg: 2

Notice how suddenly you only have to supply arg to the method, even though it's defined with two arguments? That's because a.my_method is a bound method: it's the my_method method of the a object, not that of the Foo class. It is bound to that particular instance, and has a reference to that particular object in it (if you look at dir(a.my_method), you'll notice it has a im_self attribute).

However, in your example, if you were to add a print self statement in func1, it would print foo0 instead of foo0.child. That's because the "binding" happens not when you call the method (foo0.child.secondMethod()), but simply when you reference it (foo0.func1).

More importantly, it only ever happens when you reference a method that you defined on a class.

So in your example, foo0.child.secondMethod = foo0.func1 references foo0's func1 method (which gives you a bound method of foo0), but no magic is performed in the assignment itself. It's simply setting an attribute of foo0.child.

Does that make sense?

Upvotes: 1

Related Questions