Re: Another of those is issues.
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 -- http://mail.python.org/mailman/listinfo/python-list
Re: Another of those is issues.
Thank you all for the replies! Manu -- http://mail.python.org/mailman/listinfo/python-list
Re: Another of those is issues.
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: def aFunction(): ... pass ... f = aFunction f is aFunction True --- Ok, this seems reasonable. Nevertheless, I suspect I shouldn't quite rely on it. And you're wrong. class MyClass(object): ... def myMethod(self): ... pass ... c = MyClass() m = c.myMethod m is c.myMethod 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. -- http://mail.python.org/mailman/listinfo/python-list
Re: Another of those is issues.
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 -- http://mail.python.org/mailman/listinfo/python-list
Another of those is issues.
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 -- http://mail.python.org/mailman/listinfo/python-list
Re: Another of those is issues.
Emanuele D'Arrigo manu3d at gmail.com writes: class MyClass(object): ... def myMethod(self): ... pass ... c = MyClass() m = c.myMethod m is c.myMethod 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. -- http://mail.python.org/mailman/listinfo/python-list
Re: Another of those is issues.
Emanuele D'Arrigo wrote: Hi everybody, ... f = aFunction f is aFunction True --- Ok, this seems reasonable. Nevertheless, I suspect I shouldn't quite rely on it. Actually, that's fine, you are simply comparing a pair of references class MyClass(object): ... def myMethod(self): ... pass c = MyClass() m = c.myMethod m is c.myMethod False --- What? Why is that? Method access binds the instance in dynamically. Comparing m is c.myMethod is like comparing m is partial(MyClass.method, c) --Scott David Daniels scott.dani...@acm.org -- http://mail.python.org/mailman/listinfo/python-list
Re: Another of those is issues.
On Fri, 2009-03-20 at 11:20 -0700, Emanuele D'Arrigo wrote: def aFunction(): ... pass ... f = aFunction f is aFunction 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 class MyClass(object): ... def myMethod(self): ... pass ... c = MyClass() m = c.myMethod m is c.myMethod 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 -- http://mail.python.org/mailman/listinfo/python-list
Re: Another of those is issues.
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 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. 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? 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. class C(object): ... def MyMethod(self): ... pass ... c = C() a = c.MyMethod b = c.MyMethod a, b (bound method C.MyMethod of __main__.C object at 0x7ff33fcc, bound method C.MyMethod of __main__.C object at 0x7ff33fcc) regards Steve -- Steve Holden +1 571 484 6266 +1 800 494 3119 Holden Web LLC http://www.holdenweb.com/ Want to know? Come to PyCon - soon! http://us.pycon.org/ -- http://mail.python.org/mailman/listinfo/python-list
Re: Another of those is issues.
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? 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 -- http://mail.python.org/mailman/listinfo/python-list
Re: Another of those is issues.
Terry Reedy wrote: Compare that to MyClass.myMethod is MyClass.myMethod, which is True at least in 3.0. It's true because Python 3.0 has no unbound methods. MyClass.myMethod returns the function object. It's possible to get the same behavior in 2.x: MyClass.myMethod.im_func is MyClass.myMethod.im_func Christian -- http://mail.python.org/mailman/listinfo/python-list