Another of those "is" issues.

J. Cliff Dyer jcd at sdf.lonestar.org
Tue Mar 24 15:25:10 CET 2009


On Fri, 2009-03-20 at 11:20 -0700, Emanuele D'Arrigo wrote:
> 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:
> 
> >>> def aFunction():
> ...     pass
> ...
> >>> f = aFunction
> >>> f is aFunction
> True   <--- Ok, this seems reasonable. Nevertheless, I suspect I
> shouldn't quite rely on it.
> 
> >>> class MyClass(object):
> ...     def myMethod(self):
> ...         pass
> ...
> >>> c = MyClass()
> >>> m = c.myMethod
> >>> m is c.myMethod
> 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
<unbound method TempAttributeClass.foo>
>>> c = TempAttributeClass()
>>> l = [1,2,3]
>>> x = c.foo
>>> x(l) 
3
>>> c.foo
4
>>> x == c.foo
False
>>> print x
<built-in function len>
>>> print y
<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 = A()
>>> c.x
4
>>> c.y
5
>>> c.__class__ = B
>>> # Note that neither c nor x were changed in the last step
... c.x # Class attribute found on B now
u'cow'
>>> c.y # Instance attribute: already initialized from A.__init__
5
>>> c.__init__() # Reinitialize c, now using B.__init__
>>> c.y # Re-initialized instance attribute
u'goat'


Cheers,
Cliff






More information about the Python-list mailing list