On 12/14/2011 3:01 AM, Steven D'Aprano wrote:
On Wed, 14 Dec 2011 01:29:13 -0500, Terry Reedy wrote:

To complement what Eric says below: The with statement is looking for an
instance *method*, which by definition, is a function attribute of a
*class* (the class of the context manager) that takes an instance of the
class as its first parameter.

I'm not sure that is correct... I don't think that there is anything "by
definition" about where methods live.

From the Python glossary:
"method: A function which is defined inside a class body."

That is actually a bit too narrow, as a function can be added to the class after it is defined. But the point then is that it is treated as if defined inside the class body.

Particularly not in Python where
instance methods can be attributes of the instance itself.

This is access, not definition or actual location. The glossary entry go on to say: "If called as an attribute of an instance of that class, the method will get the instance object as its first argument (which is usually called self)." This does *not* happen if a callable is found in the instance-specific dictionary. An instance method is a function (callable) attribute of a class that gets special treatment when accessed (indirectly) through an instance of that class (or subclass thereof).

class Test(object):
...     def method(self):
...             print("This method is an attribute of the class.")
...
t = Test()
t.method()
This method is an attribute of the class.

The bound method t.method is an instance the class exposed as types.MethodType. In other words, isinstance(t.method, types.MethodType) == True

import types
t.method = types.MethodType(
...     lambda self: print(
...     "This method is an attribute of the instance."), t)

Calling any old fruit an apple does not make it one.
Calling any old function a method does not make it one.

'types.MethodType' is the exposed name of the class the interpreter uses to create bound methods from a method and an instance of the class containing the method. I believe the interpreter does an isinstance check, but it must do that before calling the class, and not in the bound method constructor itself. In any case, a bound method is not a method. So the printed statement is not true.

In this case, the result is not really even a bound method, as the function argument is not a method, so we cannot even ask if the second arg is an instance of the function class container. MethodType is a special case of functools.partial, which was added later. You could have used the latter to the same effect. Or you could have used any old function that printed the same thing.

There is no relation between the object passed as the second arg of MethodType and what you do with the resulting callable. Either 't' could be something else. See below.

t.method()
This method is an attribute of the instance.

Yes, the callable (which is not a method) is (currently) an attribute of the instance. But that is irrelevant to its operation. t.method is just a callable, in particular, a pseudo bound method, not a method. It is *not* supplying the instance it is called on as the first parameter of the callable. The arguemnt (which is not used) has already been supplied. These produce the same output:

class B: pass
b = B()
b.method = t.method
b.method()

f = t.method
f()

t.method = lambda:  print("This method is an attribute of the instance.")
t.method()

So the normal lookup rules that apply to data attributes, namely
instance, then class, then superclasses, also applies to methods in
Python.

When you ask the interpreter to resolve a.x, x is just a supposed attribute, and the interpreter has no idea what class the result should be.

But this doesn't apply for special dunder attributes like __exit__, for
speed reasons.

It does not apply to dunder *methods* because they are reserved names defined to be (bound to) methods. So the interpreter knowing that it is looking for a method and that methods have to be attributes of classes, goes directly to the class.

--
Terry Jan Reedy

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to