AttributeError in "with" statement (3.2.2)

S

Steve Howell

I'm using Python 3.2.2, and the following program gives me an error
that I don't understand:

class Foo:
pass

foo = Foo()
foo.name = "Steve"

def add_goodbye_function(obj):
def goodbye():
print("goodbye " + obj.name)
obj.goodbye = goodbye

add_goodbye_function(foo)
foo.goodbye() # outputs goodbye Steve
foo.__exit__ = foo.goodbye
foo.__exit__() # outputs goodbye Steve

with foo: # fails with AttributeError: __exit__
print("doing stuff")

I am dynamically adding an attribute __exit__ to the variable foo,
which works fine when I call it directly, but it fails when I try to
use foo as the expression in the with statement. Here is the full
output:
python3 with.coffee
goodbye Steve
goodbye Steve
Traceback (most recent call last):
File "with.coffee", line 17, in <module>
with foo: # fails with AttributeError:
AttributeError: __exit__

What am I doing wrong?
 
E

Eric Snow

I'm using Python 3.2.2, and the following program gives me an error
that I don't understand:

class Foo:
 pass

foo = Foo()
foo.name = "Steve"

def add_goodbye_function(obj):
 def goodbye():
   print("goodbye " + obj.name)
 obj.goodbye = goodbye

add_goodbye_function(foo)
foo.goodbye() # outputs goodbye Steve
foo.__exit__ = foo.goodbye
foo.__exit__() # outputs goodbye Steve

with foo: # fails with AttributeError:  __exit__
 print("doing stuff")

I am dynamically adding an attribute __exit__ to the variable foo,
which works fine when I call it directly, but it fails when I try to
use foo as the expression in the with statement.  Here is the full
output:

goodbye Steve
goodbye Steve
Traceback (most recent call last):
 File "with.coffee", line 17, in <module>
   with foo: # fails with AttributeError:
AttributeError: __exit__

What am I doing wrong?

That is a tricky one.

As with many of the special methods (start and end with __) in Python,
the underlying mechanism in the interpreter is directly pulling the
function from the class object. It does not look to the instance
object for the function at any time. See
http://docs.python.org/reference/datamodel.html#special-method-lookup-for-new-style-classes.

-eric
 
T

Terry Reedy

foo.goodbye, aliased as foo.__exit__, is a *function* attribute of the
foo *instance*.

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. Notice that goodbye does not take any
parameter. The first parameter is conventionally called 'self', but that
is not a requirement. That it be there, that it expect to be bound to an
instance, and that the function be bound to the class are requirements.
 
S

Steven D'Aprano

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. Particularly not in Python where
instance methods can be attributes of the instance itself.
.... def method(self):
.... print("This method is an attribute of the class.")
........ lambda self: print(
.... "This method is an attribute of the instance."), t)This method is an attribute of the instance.


So the normal lookup rules that apply to data attributes, namely
instance, then class, then superclasses, also applies to methods in
Python. In languages that don't allow that sort of thing, like Java, you
need to use convoluted design patterns like Dynamic Proxy to make it
work. In Python, you just create a method and attach it on the instance.

http://stackoverflow.com/questions/8260740/override-a-method-for-an-
instance-of-a-class

But this doesn't apply for special dunder attributes like __exit__, for
speed reasons. (For new-style classes only, classic classes have no such
special casing. This makes automatic delegation a breeze in Python 2 with
classic classes, and a PITA in Python 3. Boo hiss.)
 
P

Peter Otten

Steve said:
I'm using Python 3.2.2, and the following program gives me an error
that I don't understand:

class Foo:
pass

foo = Foo()
foo.name = "Steve"

def add_goodbye_function(obj):
def goodbye():
print("goodbye " + obj.name)
obj.goodbye = goodbye

add_goodbye_function(foo)
foo.goodbye() # outputs goodbye Steve
foo.__exit__ = foo.goodbye
foo.__exit__() # outputs goodbye Steve

with foo: # fails with AttributeError: __exit__
print("doing stuff")

I am dynamically adding an attribute __exit__ to the variable foo,
which works fine when I call it directly, but it fails when I try to
use foo as the expression in the with statement. Here is the full
output:

goodbye Steve
goodbye Steve
Traceback (most recent call last):
File "with.coffee", line 17, in <module>
with foo: # fails with AttributeError:
AttributeError: __exit__

If you don't know it yet, contextlib.contextmanager offers a highlevel
alternative:
.... def goodbye(obj):
.... try:
.... yield obj
.... finally:
.... print("goodbye", obj.name)
........ print("hello", foo.name)
....
hello Steve
goodbye Steve
 
8

88888 Dihedral

I'm not sure that is correct... I don't think that there is anything "by
definition" about where methods live. Particularly not in Python where
instance methods can be attributes of the instance itself.

... def method(self):
... print("This method is an attribute of the class.")
...
... lambda self: print(
... "This method is an attribute of the instance."), t)
This method is an attribute of the instance.


So the normal lookup rules that apply to data attributes, namely
instance, then class, then superclasses, also applies to methods in
Python. In languages that don't allow that sort of thing, like Java, you
need to use convoluted design patterns like Dynamic Proxy to make it
work. In Python, you just create a method and attach it on the instance.

http://stackoverflow.com/questions/8260740/override-a-method-for-an-
instance-of-a-class

But this doesn't apply for special dunder attributes like __exit__, for
speed reasons. (For new-style classes only, classic classes have no such
special casing. This makes automatic delegation a breeze in Python 2 with
classic classes, and a PITA in Python 3. Boo hiss.)

In Python an instance of an object of a class can have its own method.
A living object can use those methods in the class definition and
can acquire a new method at runtime.
 
8

88888 Dihedral

In Python an instance of an object of a class can have its own method.
A living object can use those methods in the class definition and
can acquire a new method at runtime.

Therefore, it is possible for an object to build its decision tree of
actions toward a problem of various parameters in the run time.
 
E

Eric Snow

That is a tricky one.

As with many of the special methods (start and end with __) in Python,
the underlying mechanism in the interpreter is directly pulling the
function from the class object.  It does not look to the instance
object for the function at any time.  See
http://docs.python.org/reference/datamodel.html#special-method-lookup-for-new-style-classes.

-eric

Incidently, if you add the function directly to the class, everything worksout:

class Foo(object): # or "class Foo:" under Python 3
pass

foo = Foo()
foo.name = "Steve"

def add_goodbye_function(cls):
def goodbye(self, *args, **kwargs):
print("goodbye " + self.name)
cls.goodbye = goodbye

add_goodbye_function(type(foo))
foo.goodbye() # outputs goodbye Steve
Foo.__exit__ = foo.goodbye
foo.__exit__() # outputs goodbye Steve
Foo.__enter__ = (lambda self: None)

with foo:
print("doing stuff")

However, perhaps a better approach would be to put the different
pieces directly into the class:

class Foo: # python 3
def __init__(self, name):
self.name = name
def goodbye(self):
print("goodbye " + self.name)
def __enter__(self):
pass
def __exit__(self, *args, **kwargs):
self.goodbye()

foo = Foo("Steve")
foo.goodbye() # outputs goodbye Steve
foo.__exit__() # outputs goodbye Steve
with foo:
print("doing stuff")

If you want to be more dynamic about it you can do it, but it involves
black magic. Chances are really good that being explicit through your
class definition is the right approach.

-eric
 
S

Steve Howell

[...]

So the normal lookup rules that apply to data attributes, namely
instance, then class, then superclasses, also applies to methods in
Python. In languages that don't allow that sort of thing, like Java, you
need to use convoluted design patterns like Dynamic Proxy to make it
work. In Python, you just create a method and attach it on the instance.

http://stackoverflow.com/questions/8260740/override-a-method-for-an-
instance-of-a-class

But this doesn't apply for special dunder attributes like __exit__, for
speed reasons. (For new-style classes only, classic classes have no such
special casing. This makes automatic delegation a breeze in Python 2 with
classic classes, and a PITA in Python 3. Boo hiss.)


Thanks to all who responded. Basically, I think the special casing of
the "dunder" attributes threw me off.

Typically, I would just build context managers from a class, but I
wanted to experiment with instances that were built up without the
standard Python class mechanisms, instead following a Javascript-like
closure-based object creation model.

This is what I came up with:

class WithWrapper:
def __init__(self, obj):
self.obj = obj
def __enter__(self):
self.obj['enter']()
def __exit__(self, *args):
self.obj['exit'](*args)

def greeter_context(hello, goodbye):
return {
'enter': lambda: print("---\n" + hello),
'exit': lambda *args: print(goodbye)
}

gc_french = greeter_context("salut", "a plus tard")

with WithWrapper(gc_french):
print("doing stuff")

gc_slang = greeter_context("yo", "later")
with WithWrapper(gc_slang):
print("doing stuff")
 
L

Lie Ryan

If you want to be more dynamic about it you can do it, but it involves
black magic. Chances are really good that being explicit through your
class definition is the right approach.

Note that the black spice is to use the __class__ attribute:

foo.__class__.__exit__ = foo.goodbye
 
E

Eric Snow

Note that the black spice is to use the __class__ attribute:

foo.__class__.__exit__ = foo.goodbye

Good point. 'type(foo).__exit__ ...' might be even better.
Regardless, to get the dynamicism to which Steve originally referred
(make it work when added directly to the instance), you have to use a
metaclass, which is black magic[1]. However, rarely is that sort of
thing needed, so the relatively superfluous details would likely only
cloud the question at hand.

-eric

[1] I will point out that metaclasses aren't really all that bad.
They are a tool worth understanding, even if you don't use them all
the time. Understanding them also opens up a whole new world of
understanding how Python works, particularly regarding name lookup.
 
T

Terry Reedy

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).
... def method(self):
... print("This method is an attribute of the class.")
...
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
... 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.
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.
 
S

Steven D'Aprano

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.

First off, let me preface this by saying that I'm not necessarily saying
that the above glossary definition needs to be changed. For most
purposes, it is fine, since *nearly always* methods are created as
functions defined inside the class body. But it needs to be understood in
context as a simplified, hand-wavy definition which covers 99% of the
common cases, and not a precise, definitive technical definition.

To give an analogy, it is like defining mammals as "hairy animals which
give birth to live young", which is correct for all mammals except for
monotremes, which are mammals which lay eggs.

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


(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.
.... return self + a
....<class '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.


So what are methods? In Python, methods are wrappers around functions
which automatically pass the instance to the inner function object. Under
normal circumstances, you create methods by declaring functions inside a
class, but that's not the only way to create methods, and it is not the
only place they can be found.

Note that this definition can easily be adapted to describe class methods
and static methods too: class methods are wrappers around functions that
automatically pass the class to the inner function, and static methods
are wrappers which don't pass any automatic arguments.

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

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. Methods are special not because of where they are, but
because of what they are.

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. They are
functions in the sense that any callable object is a function, i.e. they
are callable sub-routines. But they're not functions in the sense of
being instances of type "function". They are wrappers around functions.

Other languages may do things differently, but that's what Python does.
You can even retrieve the function object from the wrapper:
<function func at 0xb70fd4ec>


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


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

I'm not *calling* a function a method, I'm *creating* a method from a
function object. There is no difference between a method created with
types.MethodType and a method automagically created inside a class except
for the location where the object is stored. The location is irrelevant.

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

That's an astonishing statement. What is your evidence for this amazing
claim that bound methods are not actually methods? What are they then, if
not methods?

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, but a fake that lies about returning
methods? Well, that's easy enough to test:
.... def f(self):
.... pass
....True


Unless you are going to accuse me of faking the interpreter output
(perhaps I monkey-patched the type built-in?) that is definitive proof
that types.MethodType is not fake, it actually is the method type used in
classes.

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.


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.

Of course it is. It is because I made it to be that way.

I encourage you to experiment with Python's introspection tools, perhaps
put a few calls to print inside a "pseudo bound method" (your words,
there's nothing pseudo about it) and satisfy yourself that the instance
passed is the same instance as it is called from *under the circumstances
shown*.

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()

Yes. So what? You can take a bound object and attach it to any other
object and see the same results -- this doesn't mean it isn't a bound
object. That's how bound objects work! Your objection fails because any
method will work the same way:
.... def foo(self):
.... print(self)
....<__main__.Example object at 0xb70fcc0c>

No trickery with types.MethodType, no metaclass magic, no sleight of
hand, just stock standard Python behaviour.
 
M

MRAB

First off, let me preface this by saying that I'm not necessarily saying
that the above glossary definition needs to be changed. For most
purposes, it is fine, since *nearly always* methods are created as
functions defined inside the class body. But it needs to be understood in
context as a simplified, hand-wavy definition which covers 99% of the
common cases, and not a precise, definitive technical definition.

To give an analogy, it is like defining mammals as "hairy animals which
give birth to live young", which is correct for all mammals except for
monotremes, which are mammals which lay eggs.
[snip]
Or the naked mole-rat. Or cetaceans (whales).
 
S

Steven D'Aprano

First off, let me preface this by saying that I'm not necessarily
saying that the above glossary definition needs to be changed. For most
purposes, it is fine, since *nearly always* methods are created as
functions defined inside the class body. But it needs to be understood
in context as a simplified, hand-wavy definition which covers 99% of
the common cases, and not a precise, definitive technical definition.

To give an analogy, it is like defining mammals as "hairy animals which
give birth to live young", which is correct for all mammals except for
monotremes, which are mammals which lay eggs.
[snip]
Or the naked mole-rat. Or cetaceans (whales).

Naked mole-rats and cetaceans do have hair, just not very much of it.

E.g. http://marinelife.about.com/od/cetaceans/f/whaleshair.htm
 
S

Steven D'Aprano

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.
[...]
But the definition as given is strictly wrong, because it fails to
capture the essence of what distinguishes a method from other objects.
[...]

TL;DR

http://users.rcn.com/python/download/Descriptor.htm#functions-and-methods

Short and sweet.
 
S

Steve Howell

[...]
So what are methods? In Python, methods are wrappers around functions
which automatically pass the instance to the inner function object. Under
normal circumstances, you create methods by declaring functions inside a
class, but that's not the only way to create methods, and it is not the
only place they can be found.

I've always understood methods as basically function wrappers that
pass in the instance, so it's good to hear somebody else formulate it
that way.

For the special methods like __enter__ and __exit__, the tricky part
isn't understanding what would happen once the methods were called;
the tricky part is getting them to be called in the first place, if
they were not declared inside the class or attached to the class.

import types

class Blank:
pass

foo = Blank()
foo.name = "foo1"
foo.__exit__ = types.MethodType(lambda self, *args: print(self.name),
foo)

foo.__exit__() # works like a method in python3, prints foo1
with foo:
pass
 
G

Gregory Ewing

MRAB said:
Or the naked mole-rat. Or cetaceans (whales).

The way I understand it, the main characteristic shared by
all mammals is the presence of mammary glands in females.

To wrest this back on topic, sometimes a definition
can be improved without making it any more complicated.

In the case of methods, perhaps instead of "defined inside
a class body" it could say "bound to a name in a class
namespace". That's what really matters, not how it came to
be there.
 
T

Terry Reedy

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

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.

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.

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

... return self + a
...

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.

Yes, and I can think of three other ways to make an 'add42' function.
<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.
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.
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.
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]

Not an instance method, which is the usual default meaning of 'method'
when not qualified. Sorry if you missed that and got confused.
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.
 
S

Steven D'Aprano

For the special methods like __enter__ and __exit__, the tricky part
isn't understanding what would happen once the methods were called; the
tricky part is getting them to be called in the first place, if they
were not declared inside the class or attached to the class.

If you *must* have per-instance special methods, my advice is to use a
bit of scaffolding like this:

class Whatever:
def __enter__(self, *args):
try:
enter = self.__dict__['__enter__']
except KeyError:
do_something_else() # or just let the exception occur
else:
enter(*args)


Otherwise just live with the limitation that you can override all methods
per-instance *except* dunders, and design your application accordingly.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,071
Latest member
MetabolicSolutionsKeto

Latest Threads

Top