views:

84

answers:

4

I (incorrectly?) used 'is not' in a comparison and found this curious behavior:

>>> a = 256
>>> b = int('256')
>>> c = 300
>>> d = int('300')
>>>
>>> a is not b
False
>>> c is not d
True

Obviously I should have used:

>>> a != b
False
>>> c != d
False

But it worked for a long time due to small-valued test-cases until I happened to use a number of 495.

If this is invalid syntax, then why? And shouldn't I at least get a warning?

+4  A: 

Don't use is [not] to compare integers; use == and != instead. Even though is works in current CPython for small numbers due to an optimization, it's unreliable and semantically wrong. The syntax itself is valid, but the benefits of a warning (which would have to be checked on every use of is and could be problematic with subclasses of int) are presumably not worth the trouble.

This is covered elsewhere on SO, but I didn't find it just now.

Roger Pate
+1 The language does not guarantee `is` will work for small numbers. An implementation is free to not make this optimisation
gnibbler
Yes, that's a point worth emphasizing. (However, I don't know how IronPython or Jython currently work.)
Roger Pate
+5  A: 

"is" is not a check of equality of value, but a check that two variables point to the same instance of an object.

ints and strings are confusing for this as is and == can happen to give the same result due to how the internals of the language work.

mavnn
I'm puzzled by the down vote: if my answer is inaccurate, I'd quite like to know why so I don't make the same mistakes again...
mavnn
I don't think you'll ever get an answer to your previous comment; I believe it should be obligatory to add a comment for every downvote, but who am I? So I just upvoted you.
ΤΖΩΤΖΙΟΥ
+5  A: 

For small numbers, Python is reusing the object instances, but for larger numbers, it creates new instances for them.

See this:

>>> a=256
>>> b=int('256')
>>> c=300       
>>> d=int('300')

>>> id(a)
158013588
>>> id(b)
158013588
>>> id(c)
158151472
>>> id(d)
158151436

which is exactly why a is b, but c isn't d.

adamk
A: 

For more understanding why this occurs take a look to Python-2.6.5/Objects/intobject.c:78:small_ints array and Python-2.6.5/Objects/intobject.c:1292:_PyInt_Init function in python sources.

Also similar thing occurs with lists:


>>> a = [12]
>>> id_a = id(a)
>>> del(a)
>>> id([1,2,34]) == id_a
True
>>> 

Removed lists are not destroyed. They are reused

Mykola Kharechko
id's in general are reused, whether they point to lists or not. That is why there is no reliable id->object mapping capability. But for this to happen, the first object to have that id must be deleted (as you did in `del a`). Reusing id's is not the same as the low-value-integer optimization that the OP asked about. In the OP's case, there was no deletion of the first int before the comparison of id's.
Paul McGuire
>id's in general are reusedYes.> Reusing id's is not the same as the low-value-integer optimization that the OP asked about.No, it is the same. See for usage of Python-2.6.5/Objects/listobject.c:97:free_list array
Mykola Kharechko
@Mykola - my point was that your example shows how an id gets reused when one object gets deleted and another different object gets created and recycles the same id. The OP is being confused by an implementation-dependent optimization in which multiple different names for simple immutable types get bound to the same object behind the scenes, but all the names are "alive" at the same time - none of them have been "del"ed. In fact, for this to work, the items a) must be equal, and b) must be immutable. You describe a different, but equally tricky, behavior, which can apply to any object type.
Paul McGuire