Another of those "is" issues.

E

Emanuele D'Arrigo

Hi everybody,

I was unit testing some code today and I eventually stumbled on one of
those "is" issues quickly solved replacing the "is" with "==". Still,
I don't quite see the sense of why these two cases are different:
.... pass
....True <--- Ok, this seems reasonable. Nevertheless, I suspect I
shouldn't quite rely on it.
.... def myMethod(self):
.... pass
....False <--- What? Why is that?

In my mind I was expecting that when the method is assigned to "m" all
that it happens is that its address is assigned to the name "m" so
that effectively the same address is now pointed to by two names, like
in the function case. I googled around for some hint but I wouldn't
exactly say I'm clear on the issue just yet...

Can anybody shed some light? Or point to a resource to look at? Or
what's the bit of python's source code that is responsible for dealing
with those assignments?

Manu
 
B

Benjamin Peterson

Emanuele D'Arrigo said:
... def myMethod(self):
... pass
...
False <--- What? Why is that?

Can anybody shed some light? Or point to a resource to look at? Or
what's the bit of python's source code that is responsible for dealing
with those assignments?

Functions are descriptors (they have a __get__ method). Thus, every time a
method is resolved, a new bound method object is created with the object passed
to it.
 
T

Tim Wintle

... pass
...
True <--- Ok, this seems reasonable. Nevertheless, I suspect I
shouldn't quite rely on it.

You can rely on this in the above - you've just assigned the name "f" to
the same object as "aFunction"
... def myMethod(self):
... pass
...
False <--- What? Why is that?

I believe that c.myMethod is actually a new object that's similar to
lambda self,*args,**kwargs: MyClass.myMethod(self,*args,**kwargs).

ie:

The MyClass *instance* has checked if there is an object
self.__dict__["myMethod"], and when it hasn't found it it creates a new
function which wraps the call to the base class up correctly. It's more
complicated than this though because you see the same behaviour when
checking the method on the class definition.

Seem to remember Raymond Hettinger pointing to the code that does this
as part of his "descriptor tutorial" talk at europython - but I can't
find the slides to reference.

hope that helps,

Tim Wintle
 
S

Steve Holden

Emanuele said:
Hi everybody,

I was unit testing some code today and I eventually stumbled on one of
those "is" issues quickly solved replacing the "is" with "==". Still,
I don't quite see the sense of why these two cases are different:

... pass
...

In fact, for any defined unqualified name x the assignment "n = x"
guarantees that "n is x" is True.
True <--- Ok, this seems reasonable. Nevertheless, I suspect I
shouldn't quite rely on it.
You can take that to the bank on any working Python implementation. It's
hardwired into the language's semantics.
... def myMethod(self):
... pass
...
False <--- What? Why is that?

In my mind I was expecting that when the method is assigned to "m" all
that it happens is that its address is assigned to the name "m" so
that effectively the same address is now pointed to by two names, like
in the function case. I googled around for some hint but I wouldn't
exactly say I'm clear on the issue just yet...

Can anybody shed some light? Or point to a resource to look at? Or
what's the bit of python's source code that is responsible for dealing
with those assignments?
Instance-relative references to class methods are a very special case.
They become what are called "bound methods" - the interpreter creates a
new bound method for each reference.

This allows the bound method to provide the instance as a first argument
when it is called.
.... def MyMethod(self):
.... pass
....
regards
Steve
 
S

Steve Holden

Emanuele said:
Hi everybody,

I was unit testing some code today and I eventually stumbled on one of
those "is" issues quickly solved replacing the "is" with "==". Still,
I don't quite see the sense of why these two cases are different:

... pass
...

In fact, for any defined unqualified name x the assignment "n = x"
guarantees that "n is x" is True.
True <--- Ok, this seems reasonable. Nevertheless, I suspect I
shouldn't quite rely on it.
You can take that to the bank on any working Python implementation. It's
hardwired into the language's semantics.
... def myMethod(self):
... pass
...
False <--- What? Why is that?

In my mind I was expecting that when the method is assigned to "m" all
that it happens is that its address is assigned to the name "m" so
that effectively the same address is now pointed to by two names, like
in the function case. I googled around for some hint but I wouldn't
exactly say I'm clear on the issue just yet...

Can anybody shed some light? Or point to a resource to look at? Or
what's the bit of python's source code that is responsible for dealing
with those assignments?
Instance-relative references to class methods are a very special case.
They become what are called "bound methods" - the interpreter creates a
new bound method for each reference.

This allows the bound method to provide the instance as a first argument
when it is called.
.... def MyMethod(self):
.... pass
....
regards
Steve
 
T

Terry Reedy

Emanuele said:
Hi everybody,

I was unit testing some code today and I eventually stumbled on one of
those "is" issues quickly solved replacing the "is" with "==". Still,
I don't quite see the sense of why these two cases are different:

... pass
...
True <--- Ok, this seems reasonable. Nevertheless, I suspect I
shouldn't quite rely on it.

... def myMethod(self):
... pass
...
False <--- What? Why is that?

Compare that to MyClass.myMethod is MyClass.myMethod, which is True at
least in 3.0. Repeated attribute accesses may or may not return the
same object. Remember that class (and instance) attributes can be
computed properties, or produced by whatever means in __getattr__.

Also, x.a = b; x.a==b may or may not return True as the setting might be
intercepted by __setattr__.

tjr
 
B

Bruno Desthuilliers

Emanuele D'Arrigo a écrit :
Hi everybody,

I was unit testing some code today and I eventually stumbled on one of
those "is" issues quickly solved replacing the "is" with "==". Still,
I don't quite see the sense of why these two cases are different:

... pass
...
True <--- Ok, this seems reasonable. Nevertheless, I suspect I
shouldn't quite rely on it.

And you're wrong.
... def myMethod(self):
... pass
...
False <--- What? Why is that?

c.myMethod resolves to MyClass.__dict['myMethod'].__get__(c), which
returns a new Method object.
In my mind I was expecting that when the method
is assigned to "m" all
that it happens is that its address is assigned to the name "m" so
that effectively the same address is now pointed to by two names, like
in the function case.

The "m = c.myMethod" statement effectively binds name "m" to a Method
instance. What you don't get is what really is a Method object.

You assume that the "def" statement behaves differently when used within
a "class" statement - which is just not the case. The "def" statement
_always_ create a function object. Just try this:

print type(MyClass.__dict__["myMethod"])
print type(MyClass.myMethod)
print type(c.myMethod)

What happens here is that the function type implements the descriptor
protocol in such a way to return a Method object (a callable object
wrapping the function and instance - IOW, a partial application of
MyClass.__dict__["myMethod"] and 'c') when resolved as a class
attribute. So each time you evaluate c.myMethod, you get a new Method
object.
I googled around for some hint but I wouldn't
exactly say I'm clear on the issue just yet...

Can anybody shed some light? Or point to a resource to look at?

I lost track of how many times I explained this on this newsgroup - but
googling for +method +descriptor +"lookup rules" should yield some results.
Or
what's the bit of python's source code that is responsible for dealing
with those assignments?

It has nothing to do with assignement - it's about attributes lookup rules.
 
M

Martin v. Löwis

m is c.myMethod
False <--- What? Why is that?

I think nobody has said this plainly yet (although Terry
points it out also): You cannot rely that

foo.bar is foo.bar

for any object foo and any attribute bar. In some cases,
that relation may hold, in other cases, it may not.
It depends on whether foo intercepts access to bar and
returns something different each time.

As others have explained: objects return something new
for every access to a method.

Regards,
Martin
 
J

J. Cliff Dyer

Hi everybody,

I was unit testing some code today and I eventually stumbled on one of
those "is" issues quickly solved replacing the "is" with "==". Still,
I don't quite see the sense of why these two cases are different:

... pass
...
True <--- Ok, this seems reasonable. Nevertheless, I suspect I
shouldn't quite rely on it.

... def myMethod(self):
... pass
...
False <--- What? Why is that?

In my mind I was expecting that when the method is assigned to "m" all
that it happens is that its address is assigned to the name "m" so
that effectively the same address is now pointed to by two names, like
in the function case. I googled around for some hint but I wouldn't
exactly say I'm clear on the issue just yet...

Can anybody shed some light? Or point to a resource to look at? Or
what's the bit of python's source code that is responsible for dealing
with those assignments?

Manu

So here's a f'rinstance counterexample for you:

class TempAttributeClass(object):
def __init__(self):
self.temp = True

def foo(self, x):
return len(x) + 1

def __getattribute__(self, attr):
attribute = object.__getattribute__(self,attr)
if hasattr(attribute, '__call__'):
if object.__getattribute__(self, 'temp'):
self.temp = False
return len
else:
return attribute
else:
return attribute

The first time a method is accessed from an instance of this class, it
will return len instead.
print TempAttributeClass.foo
c = TempAttributeClass()
l = [1,2,3]
x = c.foo
x(l) 3
c.foo 4
x == c.foo False
print x
<bound method TempAttributeClass.foo of <__main__.TempAttributeClass
object at 0x7f672b35e290>>

c.foo is a bound attribute, but what has it been bound to? Well, I
guess it technically, it's bound to the instance c, but what has it been
bound from? That depends first on what it encounters when traversing
its base classes, and second on how it's accessing its attributes. As
the example above shows, python is too dynamic to make any guarantees
about any of that.

Another way you could mess with that is by changing the __class__
attribute on c.

class A(object):
x = 4
def __init__(self):
self.y = 5

class B(object):
x = u'cow'
def __init__(self):
self.y = u'goat'
.... c.x # Class attribute found on B now
u'cow'u'goat'


Cheers,
Cliff
 

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,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top