I took the same quiz, and noticed the exact same. When I saw the first one, my thought was "crap, I don't remember how many integers python interns", and then I saw the next question and figured it out, but it was sort of silly that a lot of their test was stuff like this that can easily be looked up and can change by implementation (e.g. does pypy do the same? ironpython?) Plus, I was a bit mad that they wouldn't tell you which ones you got right or wrong, or even how many. Why did I bother taking it then?
Yeah, they never emailed me the scores either, and told me there was no way to know at the booth... I even joked with them that they were just trying to link it to my fb profile.
I actually didn't catch the trick until I asked someone later if they had gotten the same problem twice, "Almost, but the values were different" which is when I realized it (I blame the beer during opening night). However, as I found out today, because the way they worded the question it was all on a single line (if I remember correctly). So both cases should actually have been True, and I was right both times :P
> (e.g. does pypy do the same? ironpython?)
I wasted... ahem, spent a few hours today playing with this and found a few things, it's pretty neat!
In PyPy, `is` will always be True for primitives. This also means NaN is NaN (but still never == NaN). It doesn't use memory location for things like id() like CPython does, but it guarantees you get a unique representation. [0]
In CPython (3.5.2), it will always cache [-5, 256]. Here's the code that creates that array at startup [1] and each possible init/copy function calls this first to see if it's a cached int [2].
But it can sometimes cache other values! It also depends on if you do it from a module or the repl, and it's different depending on what scope or line each name/value is on. This one weirded out some coworkers, all from one continuous repl session:
>>> x = 500
>>> x is 500
False
>>> x = 500; x is 500
True
>>> def wat():
... x = 500
... return x is 500
>>> wat()
True
>>> x = 500
>>> def wat_wat():
... return x is 500
>>> wat_wat()
False
If you run it from a script you instead get True, True, True, False. You can disassemble it to get a hint at why. One uses STORE_FAST and LOAD_FAST, and the other LOAD_GLOBAL.
Now I'm still trying to figure out why STORE_FAST and LOAD_FAST cause it to use the same object, and why those only get called when it's interpreted together. When I figure this out I might put together a longer post about all this.
Wow, this is amazing research! I had no idea the rules actually varied this much. The same like one is especially odd. The LOAD_FAST seems to make sense because it's defined as : "Pushes a reference to the local co_varnames[var_num] onto the stack." (emphasis mine).
Also see the global case:
In [2]: x = 500
In [7]: def wat_wat_wat():
...: global x
...: x = 600
...: return x is 600
...:
In [8]: dis.disassemble(wat_wat_wat.__code__)
3 0 LOAD_CONST 1 (600)
2 STORE_GLOBAL 0 (x)
4 4 LOAD_GLOBAL 0 (x)
6 LOAD_CONST 1 (600)
8 COMPARE_OP 8 (is)
10 RETURN_VALUE
In [9]: wat_wat_wat()
Out[9]: True
But this only goes further to prove my point - it's a bit of a silly question to quiz someone on :-) For one the question didn't seem to be well defined enough, but also it tests something that no one in their right mind would do, and also I don't know if the authors realized these nuances themselves either. I guess it makes for good trivia.
Haha thanks, I was inspired by David Wolever's talk this year. This is the first time I've gone through the disassembler module or the CPython source, it's much less intimidating than I thought it would be.
Also wat! The bytecode for the global case makes sense, but I wasn't expecting True?!