AttributeError in "with" statement (3.2.2)

Terry Reedy tjreedy at udel.edu
Thu Dec 15 19:39:17 EST 2011


On 12/15/2011 12:01 AM, Steven D'Aprano wrote:
> On Wed, 14 Dec 2011 18:13:36 -0500, Terry Reedy wrote:
>
>> 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.

Note in the above that I am talking about *instance methods*.

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

Since you are disagreeing with my statement that *instance methods* are 
class attributes, you had better be claiming that *instance methods* can 
live elsewhere, else your statement has no sense.

>>    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.

Actually, I agree with Greg that rewording like he or I have suggested 
would be a good idea.

> So I'm happy for the glossary entry to stay as is, because complicating
> it would confuse the average coder more than it would educate them.

> But the definition as given is strictly wrong, because it fails to
> capture the essence of what distinguishes a method from other objects. It
> mistakes *how* you normally create a method for *what* a method is, a
> little like defining "a hamburger is a foodstuff you get from McDonalds
> by giving them money".
>
> Here are three ways that the definition fails:
>
> (1) You can create a function inside a class, and it remains a function,
> so long as the class constructor (metaclass) never gets to build a method
> object from the function. It is easy to do: just hide it inside a wrapper
> object.
>
>>>> class FunctionInsideClass(object):
> ...     def func(x, y):  # define a function inside a class
> ...             return x + 2*y
> ...     print(type(func))  # confirm is actually is a function
> ...     attr = (func,)  # hide it from the metaclass
> ...     del func

The function is not an attribute of the class, so my revised definition 
does not fail.

> <class 'function'>
>>>> print(type(FunctionInsideClass.attr[0]))
> <class 'function'>

> (2) Instead of hiding the function from the metaclass, you can change the
> metaclass to something which doesn't make methods out of functions. I
> won't show an example, because it's tricky to get right (or at least *I*
> find metaclasses tricky).

The default metaclass does not 'make methods out of functions'. Rather, 
functions that are attributes of an instance of 'type' are treated as 
methods *when accessed* via an instance of that instance. In Py 2, they 
were wrapped as unbound methods when accessed via the class, but no 
longer in Py 3, which simplifies things.

Now, perhaps you can define a metaclass that disables method behavior, 
but practically everything one say about normal Python functioning goes 
out the window when one invokes 'metaclasses' (which do not even have to 
be classes!). So I do not consider this relevant to the discussion.

> (3) So the definition is too broad: you can have functions defined inside
> classes that are not methods. But it is also too narrow: you can have
> methods outside of classes. I'm not talking about bound and unbound
> methods, but about *creating* the method from scratch outside of a class.
> When you call the method constructor directly, you can create a method
> from a function defined outside of a class.
>
>>>> def func(self, a):
> ...     return self + a
> ...
>>>> type(func)  # Definitely a function.
> <class 'function'>
>>>> obj = types.MethodType(func, 42)

As I explained before, the intended input of MethodType is an *instance 
method* and an instance of the class it is an attribute of. (Or, I 
suspect, a class method and class, which is why the appropriate check in 
each case should be outside the call. But I am sticking with instance 
methods here.) 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, which is the subject under discussion. So obj has 
nothing to do with the definition of instance method and whether I had 
any authority for the definition I gave to the OP to help him solve his 
problem.

>>>> obj(23)  # Works as expected.
> 65

Yes, and I can think of three other ways to make an 'add42' function.

>>>> type(obj)
> <class 'method'>

So what? That means 'bound method', but since your input function to 
MethodType is not a method, its output is not a bound method.

> So there's a method which has never been inside a class, and couldn't
> even if you tried: int is a built-in immutable type.

Calling it a 'method' when it is not even a bound method does not make 
it an instance method, which is the subject of discussion.

> 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.

>>> Particularly not in Python where
>>> instance methods can be attributes of the instance itself.
>>
>> This is access, not definition or actual location.
>
> Not so. In the example I gave, the method *really is* inside the
> instance, stored in the instance __dict__ and not the class __dict__.

Calling the object stored in the instance __dict__ a 'method' does not 
make it an instance method.

>> 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.
>
> That's right.

Here you agree that instance methods are special because of where they 
are and how accessed, because that is what the glossary, with my 
comment, just said.

> Methods are special not because of where they are,

Here you disagree.

>> 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).
>
> Methods aren't functions at all, not in the isinstance sense.

Please, I just specifically clarified that I meant function in the 
generic mathemetical callable sense. There is no single function class 
for there to be an 'isinstance sense'. 'Callable(f)' means 'hasattr(f, 
'__call__')

[snip]

>> '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.

Not an instance method, which is the usual default meaning of 'method' 
when not qualified. Sorry if you missed that and got confused.

>> 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.
>
> Good grief. Is it really your argument that the types.MethodType isn't
> actually the type of methods,

Good grief. As I explained, it is the type of *bound methods*. When you 
feed it an instance method and an object of the method's class, it 
outputs a bound (instance) method. When you feed it a class method and 
the corresponding class, I presume it outputs a bound (class) method. 
These are the two ways the interpreter uses it.

If a user such as you feeds it any old function and an object that has 
no relation to the function (other than its signature), then the result 
is a generic bound function and not specifically a bound method.

> but a fake that lies about returning methods?

It returns a bound method when you input a (instance/class) method, as 
the interpreter does in its routine operation.

I am baffled that you are so insistent on confusing instance methods 
with bound instance methods and bound functions. I told the OP that he 
needed instance methods and what they are and that indeed is what they 
are and what he needs.

-- 
Terry Jan Reedy




More information about the Python-list mailing list