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 classCand evalutes to a bound method. Reassigning it toc.funcassigns 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.func2calls 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
oneobject from before, I can do:To answer your question succinctly,
c.funcis bound tocifcis an instance of a class that has a methodfunc, andcdoes not itself have an instance attributefunc. (Ifcdid 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.)