5

I am trying to test a basic premise in python and it always fails and I can't figure out why.

My sys.argv looks like this:

['test.py', 'test']

And my code looks like this:

if len(sys.argv) > 1 and sys.argv[1] is 'test':
    print 'Test mode'

But the test is never true. I am sure that I am missing something really simple here, but I can't figure out what it is.

thaBadDawg
  • 5,160
  • 6
  • 35
  • 44

1 Answers1

8

As mentioned above, the main reason is your test comparison. Using is is different than using == as it compares if two objects are equal. In this case, you can verify that they are not equal by checking their ids:

import sys

print id(sys.argv[1])
print id('test')

My output:

140335994263232
140335994263424

As they point to different objects, they will not be equal when using is (but using == will compare the strings themselves, which will return True).

The issue at work here is the concept of interning. When you hardcode two identical strings into your source, the strings are interned and the two will share an object ID (this explains @SamMussmann's very valid point below). But when you pass a string in via argv, a new object is created, thereby making the comparison to an identical hardcoded string in your code return False. The best explanation I have found so far is in here, where both Alex Martelli and Jon Skeet (two very reputable sources) explain interning and when strings are interned. From these explanations, it does seem that since the data from argv is external to the program, the values aren't interned, and therefore have different object IDs than if they were both literals in the source.

One additional point of interest (unrelated to the issue at hand but pertinent to the is discussion) is the caching that is done with numbers. The numbers from -5 to 256 are cached, meaning that is comparisons with equal numbers in that range will be True, regardless of how they are calculated:

In [1]: 256 is 255 + 1
Out[1]: True

In [2]: 257 is 256 + 1
Out[2]: False

In [3]: -5 is -4 - 1
Out[3]: True

In [4]: -6 is -5 - 1
Out[4]: False
Community
  • 1
  • 1
RocketDonkey
  • 36,383
  • 7
  • 80
  • 84
  • 1
    There's something funny about `sys.argv` here, because if I put `'test'` in a regular list, then `a[0] is 'test'` is true... – Sam Mussmann Nov 22 '12 at 01:25
  • @SamMussmann: Thank you (I have the same question). I think what might be happening is that the python interpreter gets its own sandbox to play with, which might explain this behavior -- objects in `sys.argv` are created before the invocation of the python interpreter and therefore live in a memory space outside this sandbox. <-- I have no idea if that's correct. Could someone confirm/deny please? – inspectorG4dget Nov 22 '12 at 01:28
  • 2
    @SamMussmann That's because in your test you used string literals, that are interned. Python put the same interned string in the list, so they are in fact the same object. So the test Passes. But that is not the proper use of the `is` operator. Use `==` when you want to compare equality. – Keith Nov 22 '12 at 01:32
  • @SamMussmann inpectorG4dget Does the above edit make logical sense to you guys? I want to find better docs on it as I'm not 100% sure, but I think it matches the behavior we are seeing. – RocketDonkey Nov 22 '12 at 01:32
  • Yep! And appending `test` to `sys.argv` does *not* give it a different id -- that was a user error... – Sam Mussmann Nov 22 '12 at 01:34
  • @SamMussmann Haha, alright cool, one less edge case to investigate :) – RocketDonkey Nov 22 '12 at 01:35
  • Cool! Also, if you call `id(intern(sys.argv[1]))`, you'll get a value that's the same as `id('test')` – Sam Mussmann Nov 22 '12 at 01:55
  • The edit makes sense; and those are excellent references. I traced it back a ways, and it seems to be applicable here. – inspectorG4dget Nov 22 '12 at 01:59
  • @inspectorG4dget Awesome, thanks for confirming (I trust your opinion a lot as well :) ). – RocketDonkey Nov 22 '12 at 02:25