class C:
def func(self, a):
print(a)
c = C()
print(c.__dict__) # {}
c.func = c.func
# c.func is now an instance attribute, which hides instance method
print(c.__dict__) # {'func' : (bound method C.f of (__main.C object at ...))}
# how come it still works as if it's an instance method:
c.func(1) # prints 1
# with a regular function assigned to c.func, this won't happen:
def newfunc(self, a):
print(a)
c.func = newfunc
c.func(1) # TypeError
What are the conditions under which a call c.func(1)
magically adds the instance c
as the first parameter? I thought it only happens when func
was defined as an instance method in the class. But it seems it sometimes works when func
is just an instance attribute.
The binding to the instance happens when you access
c.func
, not when you call it. When you doc.func = c.func
, the attribute access on the right-hand side is handled by the classC
and evalutes to a bound method. Reassigning it toc.func
assigns it to the instance, but it's already a bound method, so this doesn't change the behavior.In your newfunc example, you just defined a function and assigned it, so it never got made into an instance method. Since you assign it directly to an instance, the class doesn't get a chance to intercept the access and wrap it in an instancemethod.
You can assign a bound method anywhere you like and it remains bound to the instance via which you got it. See these examples:
Note that once I assigned
one.func2 = two.func
, callingone.func2
calls an instance method bound totwo
, notone
. This is because when I accessedtwo.func
, I got a method bound to that instance. It doesn't matter where I assign it later. It remains bound to the instance via which it was accessed.If you access the unbound method directly and try to assign it elsewhere, then you get the TypeError because the method was never bound:
Also note that if you add the method to the class, it will work when called on an instance, even if the instance was created before you added the method to the class. Keeping the same
one
object from before, I can do:To answer your question succinctly,
c.func
is bound toc
ifc
is an instance of a class that has a methodfunc
, andc
does not itself have an instance attributefunc
. (Ifc
did have an instance attribute of the same name, that overrides the class one, so the class doesn't get to do its wrapping to bind the method to the instance.)