On Thu, 15 Dec 2011 19:39:17 -0500, Terry Reedy wrote: [...] After reading your post, I think I have worked out where our disagreement lines: you think that bound methods and instance methods are not the same thing, and that a function defined inside a class is different from a function outside of a class.
For example, you say: > If so, the output is a *bound method*. In your example > above, func is not an instance method and obj is not a bound method. It > is simply an partially evaluated curried function or if you prefer, a > bound function. Take you pick, or make up your own term, but it is NOT > an instance method, and later on: >> So what are methods? In Python, methods are wrappers around functions >> which automatically pass the instance to the inner function object. > > These are bound methods. The instance methods are the functions wrapped. I am afraid you are mistaken. What you say may very well apply to other languages, but in Python, def creates functions no matter where you execute it. Always and without exception. I admit to an earlier mistake: I thought that conversion from function to method occurred once, when the class statement was executed, but I was mistaken. Re-reading Raymond Hettinger's excellent article on the descriptor protocol reminded me that methods are created on an as-needed basis, at runtime. Back to methods and def. So let's see what happens in Python 3.1: >>> def spam(self): # Clearly a function. ... pass ... >>> class K(object): ... def ham(self): # Allegedly an instance method ... pass ... According to your various statements, spam is a function and ham is an instance method. Merely storing a outside function inside a class doesn't create an instance method, it creates what you call a bound method (although I'm unclear what you think the difference is). So let's see what happens when we compare an alleged "bound method that is not an instance method" with an actual instance method: >>> K.spam = spam >>> type(K.spam) is type(K.ham) True Their types are identical. We get the same thing when we compare an actual function with a function object created inside a class, which you claim is actually an instance method: >>> type(spam) is type(K.__dict__['ham']) # Bypass descriptor protocol True Nor is there any difference in type between bound and unbound methods: they are both instance methods differing only in whether or not they have the first argument "self" available. Bound is an adjective, not part of the type: small method, obfuscated method, buggy method, or bound method. Python 3 no longer uses unbound methods, since they are functionally identical to the unwrapped function, so this snippet is from Python 2.6: py> k = K() py> type(k.ham) is type(K().ham) # bound vs bound True py> type(k.ham) is type(K.ham) # bound vs unbound True In Python, instance methods are wrappers around function objects; they are created on call, and generally do not exist *anywhere* until needed, or if you store a reference to them. >>> k = K() >>> a = k.ham >>> b = k.ham >>> a is b False Under normal circumstances, the only persistent object is the function, which you can extract from the (class or instance) __dict__ or the method wrapper: >>> a.__func__ is K.__dict__['ham'] True Methods are created by the descriptor protocol: when you use the normal a.b syntax to access K.__dict__['ham'], the metaclass calls the __get__ method of function ham. If you call it from an instance, you get a method object bound to that instance. If you call it from the class, in Python 2 you get a method object not bound to an instance, in Python 3 you get the function without a wrapper. [Aside: class methods and static methods also work the same way, via __get__ and the descriptor protocol, but behave differently. Class methods reuse the instance method type, which is somewhat confusing, and static methods just return the function.] So, what the hell does all this mean in practice? Most of the time, absolutely nothing. That's why I started this discussion with the disclaimer that I didn't think it was worth changing the (not quite right) definition of "method" you originally quoted. Most of the time, we only access methods via the instance.method syntax, which gives us an instance method. By the principle that if it quacks like a duck and swims like a duck, it is good enough to call it a duck, I'm happy to agree that ham here is a method: class K: def ham(self): pass That covers 99% of all use-cases and is good enough for most situations. But the OP ran into one of those edge cases in the 1%, where things are not so simple. He was attempting to create an instance method called "__exit__" and store it in the instance __dict__ instead of the class __dict__. This is a perfectly reasonable thing to do, but there are two gotchas to it: * The descriptor protocol doesn't get used for instance lookups. That's why you can't stick properties in an instance, only in the class. * CPython, and I believe Jython, don't look up special dunder methods like __exit__ on the instance, only on the class. So even if the OP manually created an instance method rather than relying on the descriptor protocol, his per-instance __exit__ would not be called without extra work. -- Steven -- http://mail.python.org/mailman/listinfo/python-list