Why does the `is` operator behave differently in a script vs the REPL?Python 3.6.5 “is” and “==” for integers beyond caching intervalDifferent evaluation for object equality when running Python with file or in a shellUnderstanding Python's “is” operatorConfused about `is` operator with stringsPython 3 integer addresses“is” operator behaves unexpectedly with integersHow do I return multiple values from a function?Does Python have a ternary conditional operator?How do you get the logical xor of two variables in Python?Why does comparing strings using either '==' or 'is' sometimes produce a different result?Running shell command and capturing the outputCreating a singleton in PythonWhy does Python code run faster in a function?Why is “1000000000000000 in range(1000000000000001)” so fast in Python 3?Why is [] faster than list()?
Integer Lists of Noah
LED glows slightly during soldering
Is that a case of "DOUBLE-NEGATIVES" as claimed by Grammarly?
Is it OK to leave real names & info visible in business card portfolio?
Distinguish the explanations of Galadriel's test in LotR
The origin of a particular self-reference paradox
Can I play a mimic PC?
Postgres trigram match acting strange for specific characters
How can I effectively communicate to recruiters that a phone call is not possible?
Can i use larger/smaller circular saw blades on my circular / plunge / table / miter saw?
Through: how to use it with subtraction of functions?
What is the correct parsing of お高くとまる?
How do we handle pauses in a dialogue?
Why is a mixture of two normally distributed variables only bimodal if their means differ by at least two times the common standard deviation?
Why weren't bootable game disks ever a thing on the IBM PC?
Is there a nice way to implement a conditional type with default fail case?
Chorophyll and photosynthesis in plants with coloured leaves
Misrepresented my work history
When I press the space bar it deletes the letters in front of it
What happens when adult Billy Batson says "Shazam"?
Why does the Antonov AN-225 not have any winglets?
Graduate student with abysmal English writing skills, how to help
What is the parallel of Day of the Dead with Stranger things?
What is a "Lear Processor" and how did it work?
Why does the `is` operator behave differently in a script vs the REPL?
Python 3.6.5 “is” and “==” for integers beyond caching intervalDifferent evaluation for object equality when running Python with file or in a shellUnderstanding Python's “is” operatorConfused about `is` operator with stringsPython 3 integer addresses“is” operator behaves unexpectedly with integersHow do I return multiple values from a function?Does Python have a ternary conditional operator?How do you get the logical xor of two variables in Python?Why does comparing strings using either '==' or 'is' sometimes produce a different result?Running shell command and capturing the outputCreating a singleton in PythonWhy does Python code run faster in a function?Why is “1000000000000000 in range(1000000000000001)” so fast in Python 3?Why is [] faster than list()?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
In python, two codes have different results:
a = 300
b = 300
print (a==b)
print (a is b) ## print True
print ("id(a) = %d, id(b) = %d"%(id(a), id(b))) ## They have same address
But in shell mode(interactive mode):
>>> a = 300
>>> b = 300
>>> a is b
False
>>> id(a)
4501364368
>>> id(b)
4501362224
"is" operator has different results.
python cpython
|
show 8 more comments
In python, two codes have different results:
a = 300
b = 300
print (a==b)
print (a is b) ## print True
print ("id(a) = %d, id(b) = %d"%(id(a), id(b))) ## They have same address
But in shell mode(interactive mode):
>>> a = 300
>>> b = 300
>>> a is b
False
>>> id(a)
4501364368
>>> id(b)
4501362224
"is" operator has different results.
python cpython
1
@Aran-Fey: it's not all that important what type of object is being compared this way, but in this particular case it's because the entity that compiled the.py
source file noticed that 300 == 300 so made only one 300 instance, while the interpreter that read the>>>
lines didn't notice that 300 == 300 so made two separate 300 instances.
– torek
Mar 25 at 23:05
3
Still,is
is for object identity. Only use it when that's what you want.
– Chris
Mar 25 at 23:05
3
@Chris I don't think that really answers the question, either. Like torek said, this question is about the difference between running the same code in the REPL vs as a script.
– Aran-Fey
Mar 25 at 23:06
5
As @Chris says. In other words identity of two immutable objects with the same value is incidental and not guaranteed / required. Why it once matches and once doesn't is hence immaterial.
– Ondrej K.
Mar 25 at 23:14
1
Folks, none of those answers actually answers the question. :-(
– John Szakmeister
Mar 25 at 23:34
|
show 8 more comments
In python, two codes have different results:
a = 300
b = 300
print (a==b)
print (a is b) ## print True
print ("id(a) = %d, id(b) = %d"%(id(a), id(b))) ## They have same address
But in shell mode(interactive mode):
>>> a = 300
>>> b = 300
>>> a is b
False
>>> id(a)
4501364368
>>> id(b)
4501362224
"is" operator has different results.
python cpython
In python, two codes have different results:
a = 300
b = 300
print (a==b)
print (a is b) ## print True
print ("id(a) = %d, id(b) = %d"%(id(a), id(b))) ## They have same address
But in shell mode(interactive mode):
>>> a = 300
>>> b = 300
>>> a is b
False
>>> id(a)
4501364368
>>> id(b)
4501362224
"is" operator has different results.
python cpython
python cpython
edited Mar 25 at 23:38
wim
176k58 gold badges345 silver badges475 bronze badges
176k58 gold badges345 silver badges475 bronze badges
asked Mar 25 at 23:00
Bokyun NaBokyun Na
211 bronze badge
211 bronze badge
1
@Aran-Fey: it's not all that important what type of object is being compared this way, but in this particular case it's because the entity that compiled the.py
source file noticed that 300 == 300 so made only one 300 instance, while the interpreter that read the>>>
lines didn't notice that 300 == 300 so made two separate 300 instances.
– torek
Mar 25 at 23:05
3
Still,is
is for object identity. Only use it when that's what you want.
– Chris
Mar 25 at 23:05
3
@Chris I don't think that really answers the question, either. Like torek said, this question is about the difference between running the same code in the REPL vs as a script.
– Aran-Fey
Mar 25 at 23:06
5
As @Chris says. In other words identity of two immutable objects with the same value is incidental and not guaranteed / required. Why it once matches and once doesn't is hence immaterial.
– Ondrej K.
Mar 25 at 23:14
1
Folks, none of those answers actually answers the question. :-(
– John Szakmeister
Mar 25 at 23:34
|
show 8 more comments
1
@Aran-Fey: it's not all that important what type of object is being compared this way, but in this particular case it's because the entity that compiled the.py
source file noticed that 300 == 300 so made only one 300 instance, while the interpreter that read the>>>
lines didn't notice that 300 == 300 so made two separate 300 instances.
– torek
Mar 25 at 23:05
3
Still,is
is for object identity. Only use it when that's what you want.
– Chris
Mar 25 at 23:05
3
@Chris I don't think that really answers the question, either. Like torek said, this question is about the difference between running the same code in the REPL vs as a script.
– Aran-Fey
Mar 25 at 23:06
5
As @Chris says. In other words identity of two immutable objects with the same value is incidental and not guaranteed / required. Why it once matches and once doesn't is hence immaterial.
– Ondrej K.
Mar 25 at 23:14
1
Folks, none of those answers actually answers the question. :-(
– John Szakmeister
Mar 25 at 23:34
1
1
@Aran-Fey: it's not all that important what type of object is being compared this way, but in this particular case it's because the entity that compiled the
.py
source file noticed that 300 == 300 so made only one 300 instance, while the interpreter that read the >>>
lines didn't notice that 300 == 300 so made two separate 300 instances.– torek
Mar 25 at 23:05
@Aran-Fey: it's not all that important what type of object is being compared this way, but in this particular case it's because the entity that compiled the
.py
source file noticed that 300 == 300 so made only one 300 instance, while the interpreter that read the >>>
lines didn't notice that 300 == 300 so made two separate 300 instances.– torek
Mar 25 at 23:05
3
3
Still,
is
is for object identity. Only use it when that's what you want.– Chris
Mar 25 at 23:05
Still,
is
is for object identity. Only use it when that's what you want.– Chris
Mar 25 at 23:05
3
3
@Chris I don't think that really answers the question, either. Like torek said, this question is about the difference between running the same code in the REPL vs as a script.
– Aran-Fey
Mar 25 at 23:06
@Chris I don't think that really answers the question, either. Like torek said, this question is about the difference between running the same code in the REPL vs as a script.
– Aran-Fey
Mar 25 at 23:06
5
5
As @Chris says. In other words identity of two immutable objects with the same value is incidental and not guaranteed / required. Why it once matches and once doesn't is hence immaterial.
– Ondrej K.
Mar 25 at 23:14
As @Chris says. In other words identity of two immutable objects with the same value is incidental and not guaranteed / required. Why it once matches and once doesn't is hence immaterial.
– Ondrej K.
Mar 25 at 23:14
1
1
Folks, none of those answers actually answers the question. :-(
– John Szakmeister
Mar 25 at 23:34
Folks, none of those answers actually answers the question. :-(
– John Szakmeister
Mar 25 at 23:34
|
show 8 more comments
2 Answers
2
active
oldest
votes
When you run code in a .py
script, the entire file is compiled into a code object before executing it. In this case, CPython is able to make certain optimizations - like reusing the same instance for the integer 300.
You could also reproduce that in the REPL, by executing code in a context more closely resembling the execution of a script:
>>> source = """
... a = 300
... b = 300
... print (a==b)
... print (a is b)## print True
... print ("id(a) = %d, id(b) = %d"%(id(a), id(b))) ## They have same address
... """
>>> code_obj = compile(source, filename="myscript.py", mode="exec")
>>> exec(code_obj)
True
True
id(a) = 140736953597776, id(b) = 140736953597776
Some of these optimizations are pretty aggressive. You could modify the script line b = 300
changing it to b = 150 + 150
, and CPython would still "fold" b
into the same constant. If you're interested in such implementation details, look in peephole.c
and Ctrl+F for the "consts table".
In contrast, when you run code line-by-line directly in the REPL it executes in a different context. Each line is compiled in "single" mode and this optimization is not available.
>>> scope =
>>> lines = source.splitlines()
>>> for line in lines:
... code_obj = compile(line, filename="<I'm in the REPL, yo!>", mode="single")
... exec(code_obj, scope)
...
True
False
id(a) = 140737087176016, id(b) = 140737087176080
>>> scope['a'], scope['b']
(300, 300)
>>> id(scope['a']), id(scope['b'])
(140737087176016, 140737087176080)
There is no optimization forb=150+150
in Python3.6, but with later versions.
– ead
Mar 26 at 20:56
add a comment |
There are actually two things to know about CPython and its behavior here.
First, small integers in the range of [-5, 256] are interned internally.
So any value falling in that range will share the same id, even at the REPL:
>>> a = 100
>>> b = 100
>>> a is b
True
Since 300 > 256, it's not being interned:
>>> a = 300
>>> b = 300
>>> a is b
False
Second, is that in a script, literals are put into a constant section of the
compiled code. Python is smart enough to realize that since both a
and b
refer to the literal 300
and that 300
is an immutable object, it can just
go ahead and reference the same constant location. If you tweak your script
a bit and write it as:
def foo():
a = 300
b = 300
print(a==b)
print(a is b)
print("id(a) = %d, id(b) = %d" % (id(a), id(b)))
import dis
dis.disassemble(foo.__code__)
The beginning part of the output looks like this:
2 0 LOAD_CONST 1 (300)
2 STORE_FAST 0 (a)
3 4 LOAD_CONST 1 (300)
6 STORE_FAST 1 (b)
...
As you can see, CPython is loading the a
and b
using the same constant slot.
This means that a
and b
are now referring to the same object (because they
reference the same slot) and that is why a is b
is True
in the script but
not at the REPL.
You can see this behavior in the REPL too, if you wrap your statements in a function:
>>> import dis
>>> def foo():
... a = 300
... b = 300
... print(a==b)
... print(a is b)
... print("id(a) = %d, id(b) = %d" % (id(a), id(b)))
...
>>> foo()
True
True
id(a) = 4369383056, id(b) = 4369383056
>>> dis.disassemble(foo.__code__)
2 0 LOAD_CONST 1 (300)
2 STORE_FAST 0 (a)
3 4 LOAD_CONST 1 (300)
6 STORE_FAST 1 (b)
# snipped...
Bottom line: while CPython makes these optimizations at times, you shouldn't really count on it--it's really an implementation detail, and one that they've changed over time (CPython used to only do this for integers up to 100, for example). If you're comparing numbers, use ==
. :-)
This answer leaves a lot to be desired: by "tweaking the script" you're putting the code into a function scope (local vars) instead of a module scope (global vars), which - in terms of execution and name resolution - is now a completely different scoping situation. I think that somewhat invalidates the disassembly. Yes, it happens that the same peephole optimizer is used over the module code in the same way it's used over a function body, but that certainly doesn't have to be the case. And the answer also hasn't really explained why the same optimization is not done directly at the REPL.
– wim
Mar 26 at 15:51
@wim I certainly delete the answer if you don't think it meets the mark. I personally don't feel the tweaking was negative, but I hear you--it's not exactly the same. But I think all of this is pretty tied to what Python does today and isn't a guarantee of what Python may do in the future. And that's a fair criticism--I did not explicitly state why line by line at the REPL is different.
– John Szakmeister
Mar 26 at 16:55
Oh, I don't think you should delete it, just update it! I do think the disassembly demonstration is useful content. However, it shoulddis
module scoped code, not function scoped code - recent versions of dis allow you to pass in a string of source directly, so it's not necessary to use a function object's__code__
attribute.
– wim
Mar 26 at 17:16
@wim That doesn't seem very fair to you--I think you've done the work on that front already and your answer should be the accepted answer (though I think adding the interning of small integers to the answer is good, just in case people try and get results they don't expect).
– John Szakmeister
Mar 27 at 21:09
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55347581%2fwhy-does-the-is-operator-behave-differently-in-a-script-vs-the-repl%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
When you run code in a .py
script, the entire file is compiled into a code object before executing it. In this case, CPython is able to make certain optimizations - like reusing the same instance for the integer 300.
You could also reproduce that in the REPL, by executing code in a context more closely resembling the execution of a script:
>>> source = """
... a = 300
... b = 300
... print (a==b)
... print (a is b)## print True
... print ("id(a) = %d, id(b) = %d"%(id(a), id(b))) ## They have same address
... """
>>> code_obj = compile(source, filename="myscript.py", mode="exec")
>>> exec(code_obj)
True
True
id(a) = 140736953597776, id(b) = 140736953597776
Some of these optimizations are pretty aggressive. You could modify the script line b = 300
changing it to b = 150 + 150
, and CPython would still "fold" b
into the same constant. If you're interested in such implementation details, look in peephole.c
and Ctrl+F for the "consts table".
In contrast, when you run code line-by-line directly in the REPL it executes in a different context. Each line is compiled in "single" mode and this optimization is not available.
>>> scope =
>>> lines = source.splitlines()
>>> for line in lines:
... code_obj = compile(line, filename="<I'm in the REPL, yo!>", mode="single")
... exec(code_obj, scope)
...
True
False
id(a) = 140737087176016, id(b) = 140737087176080
>>> scope['a'], scope['b']
(300, 300)
>>> id(scope['a']), id(scope['b'])
(140737087176016, 140737087176080)
There is no optimization forb=150+150
in Python3.6, but with later versions.
– ead
Mar 26 at 20:56
add a comment |
When you run code in a .py
script, the entire file is compiled into a code object before executing it. In this case, CPython is able to make certain optimizations - like reusing the same instance for the integer 300.
You could also reproduce that in the REPL, by executing code in a context more closely resembling the execution of a script:
>>> source = """
... a = 300
... b = 300
... print (a==b)
... print (a is b)## print True
... print ("id(a) = %d, id(b) = %d"%(id(a), id(b))) ## They have same address
... """
>>> code_obj = compile(source, filename="myscript.py", mode="exec")
>>> exec(code_obj)
True
True
id(a) = 140736953597776, id(b) = 140736953597776
Some of these optimizations are pretty aggressive. You could modify the script line b = 300
changing it to b = 150 + 150
, and CPython would still "fold" b
into the same constant. If you're interested in such implementation details, look in peephole.c
and Ctrl+F for the "consts table".
In contrast, when you run code line-by-line directly in the REPL it executes in a different context. Each line is compiled in "single" mode and this optimization is not available.
>>> scope =
>>> lines = source.splitlines()
>>> for line in lines:
... code_obj = compile(line, filename="<I'm in the REPL, yo!>", mode="single")
... exec(code_obj, scope)
...
True
False
id(a) = 140737087176016, id(b) = 140737087176080
>>> scope['a'], scope['b']
(300, 300)
>>> id(scope['a']), id(scope['b'])
(140737087176016, 140737087176080)
There is no optimization forb=150+150
in Python3.6, but with later versions.
– ead
Mar 26 at 20:56
add a comment |
When you run code in a .py
script, the entire file is compiled into a code object before executing it. In this case, CPython is able to make certain optimizations - like reusing the same instance for the integer 300.
You could also reproduce that in the REPL, by executing code in a context more closely resembling the execution of a script:
>>> source = """
... a = 300
... b = 300
... print (a==b)
... print (a is b)## print True
... print ("id(a) = %d, id(b) = %d"%(id(a), id(b))) ## They have same address
... """
>>> code_obj = compile(source, filename="myscript.py", mode="exec")
>>> exec(code_obj)
True
True
id(a) = 140736953597776, id(b) = 140736953597776
Some of these optimizations are pretty aggressive. You could modify the script line b = 300
changing it to b = 150 + 150
, and CPython would still "fold" b
into the same constant. If you're interested in such implementation details, look in peephole.c
and Ctrl+F for the "consts table".
In contrast, when you run code line-by-line directly in the REPL it executes in a different context. Each line is compiled in "single" mode and this optimization is not available.
>>> scope =
>>> lines = source.splitlines()
>>> for line in lines:
... code_obj = compile(line, filename="<I'm in the REPL, yo!>", mode="single")
... exec(code_obj, scope)
...
True
False
id(a) = 140737087176016, id(b) = 140737087176080
>>> scope['a'], scope['b']
(300, 300)
>>> id(scope['a']), id(scope['b'])
(140737087176016, 140737087176080)
When you run code in a .py
script, the entire file is compiled into a code object before executing it. In this case, CPython is able to make certain optimizations - like reusing the same instance for the integer 300.
You could also reproduce that in the REPL, by executing code in a context more closely resembling the execution of a script:
>>> source = """
... a = 300
... b = 300
... print (a==b)
... print (a is b)## print True
... print ("id(a) = %d, id(b) = %d"%(id(a), id(b))) ## They have same address
... """
>>> code_obj = compile(source, filename="myscript.py", mode="exec")
>>> exec(code_obj)
True
True
id(a) = 140736953597776, id(b) = 140736953597776
Some of these optimizations are pretty aggressive. You could modify the script line b = 300
changing it to b = 150 + 150
, and CPython would still "fold" b
into the same constant. If you're interested in such implementation details, look in peephole.c
and Ctrl+F for the "consts table".
In contrast, when you run code line-by-line directly in the REPL it executes in a different context. Each line is compiled in "single" mode and this optimization is not available.
>>> scope =
>>> lines = source.splitlines()
>>> for line in lines:
... code_obj = compile(line, filename="<I'm in the REPL, yo!>", mode="single")
... exec(code_obj, scope)
...
True
False
id(a) = 140737087176016, id(b) = 140737087176080
>>> scope['a'], scope['b']
(300, 300)
>>> id(scope['a']), id(scope['b'])
(140737087176016, 140737087176080)
edited Mar 25 at 23:59
answered Mar 25 at 23:39
wimwim
176k58 gold badges345 silver badges475 bronze badges
176k58 gold badges345 silver badges475 bronze badges
There is no optimization forb=150+150
in Python3.6, but with later versions.
– ead
Mar 26 at 20:56
add a comment |
There is no optimization forb=150+150
in Python3.6, but with later versions.
– ead
Mar 26 at 20:56
There is no optimization for
b=150+150
in Python3.6, but with later versions.– ead
Mar 26 at 20:56
There is no optimization for
b=150+150
in Python3.6, but with later versions.– ead
Mar 26 at 20:56
add a comment |
There are actually two things to know about CPython and its behavior here.
First, small integers in the range of [-5, 256] are interned internally.
So any value falling in that range will share the same id, even at the REPL:
>>> a = 100
>>> b = 100
>>> a is b
True
Since 300 > 256, it's not being interned:
>>> a = 300
>>> b = 300
>>> a is b
False
Second, is that in a script, literals are put into a constant section of the
compiled code. Python is smart enough to realize that since both a
and b
refer to the literal 300
and that 300
is an immutable object, it can just
go ahead and reference the same constant location. If you tweak your script
a bit and write it as:
def foo():
a = 300
b = 300
print(a==b)
print(a is b)
print("id(a) = %d, id(b) = %d" % (id(a), id(b)))
import dis
dis.disassemble(foo.__code__)
The beginning part of the output looks like this:
2 0 LOAD_CONST 1 (300)
2 STORE_FAST 0 (a)
3 4 LOAD_CONST 1 (300)
6 STORE_FAST 1 (b)
...
As you can see, CPython is loading the a
and b
using the same constant slot.
This means that a
and b
are now referring to the same object (because they
reference the same slot) and that is why a is b
is True
in the script but
not at the REPL.
You can see this behavior in the REPL too, if you wrap your statements in a function:
>>> import dis
>>> def foo():
... a = 300
... b = 300
... print(a==b)
... print(a is b)
... print("id(a) = %d, id(b) = %d" % (id(a), id(b)))
...
>>> foo()
True
True
id(a) = 4369383056, id(b) = 4369383056
>>> dis.disassemble(foo.__code__)
2 0 LOAD_CONST 1 (300)
2 STORE_FAST 0 (a)
3 4 LOAD_CONST 1 (300)
6 STORE_FAST 1 (b)
# snipped...
Bottom line: while CPython makes these optimizations at times, you shouldn't really count on it--it's really an implementation detail, and one that they've changed over time (CPython used to only do this for integers up to 100, for example). If you're comparing numbers, use ==
. :-)
This answer leaves a lot to be desired: by "tweaking the script" you're putting the code into a function scope (local vars) instead of a module scope (global vars), which - in terms of execution and name resolution - is now a completely different scoping situation. I think that somewhat invalidates the disassembly. Yes, it happens that the same peephole optimizer is used over the module code in the same way it's used over a function body, but that certainly doesn't have to be the case. And the answer also hasn't really explained why the same optimization is not done directly at the REPL.
– wim
Mar 26 at 15:51
@wim I certainly delete the answer if you don't think it meets the mark. I personally don't feel the tweaking was negative, but I hear you--it's not exactly the same. But I think all of this is pretty tied to what Python does today and isn't a guarantee of what Python may do in the future. And that's a fair criticism--I did not explicitly state why line by line at the REPL is different.
– John Szakmeister
Mar 26 at 16:55
Oh, I don't think you should delete it, just update it! I do think the disassembly demonstration is useful content. However, it shoulddis
module scoped code, not function scoped code - recent versions of dis allow you to pass in a string of source directly, so it's not necessary to use a function object's__code__
attribute.
– wim
Mar 26 at 17:16
@wim That doesn't seem very fair to you--I think you've done the work on that front already and your answer should be the accepted answer (though I think adding the interning of small integers to the answer is good, just in case people try and get results they don't expect).
– John Szakmeister
Mar 27 at 21:09
add a comment |
There are actually two things to know about CPython and its behavior here.
First, small integers in the range of [-5, 256] are interned internally.
So any value falling in that range will share the same id, even at the REPL:
>>> a = 100
>>> b = 100
>>> a is b
True
Since 300 > 256, it's not being interned:
>>> a = 300
>>> b = 300
>>> a is b
False
Second, is that in a script, literals are put into a constant section of the
compiled code. Python is smart enough to realize that since both a
and b
refer to the literal 300
and that 300
is an immutable object, it can just
go ahead and reference the same constant location. If you tweak your script
a bit and write it as:
def foo():
a = 300
b = 300
print(a==b)
print(a is b)
print("id(a) = %d, id(b) = %d" % (id(a), id(b)))
import dis
dis.disassemble(foo.__code__)
The beginning part of the output looks like this:
2 0 LOAD_CONST 1 (300)
2 STORE_FAST 0 (a)
3 4 LOAD_CONST 1 (300)
6 STORE_FAST 1 (b)
...
As you can see, CPython is loading the a
and b
using the same constant slot.
This means that a
and b
are now referring to the same object (because they
reference the same slot) and that is why a is b
is True
in the script but
not at the REPL.
You can see this behavior in the REPL too, if you wrap your statements in a function:
>>> import dis
>>> def foo():
... a = 300
... b = 300
... print(a==b)
... print(a is b)
... print("id(a) = %d, id(b) = %d" % (id(a), id(b)))
...
>>> foo()
True
True
id(a) = 4369383056, id(b) = 4369383056
>>> dis.disassemble(foo.__code__)
2 0 LOAD_CONST 1 (300)
2 STORE_FAST 0 (a)
3 4 LOAD_CONST 1 (300)
6 STORE_FAST 1 (b)
# snipped...
Bottom line: while CPython makes these optimizations at times, you shouldn't really count on it--it's really an implementation detail, and one that they've changed over time (CPython used to only do this for integers up to 100, for example). If you're comparing numbers, use ==
. :-)
This answer leaves a lot to be desired: by "tweaking the script" you're putting the code into a function scope (local vars) instead of a module scope (global vars), which - in terms of execution and name resolution - is now a completely different scoping situation. I think that somewhat invalidates the disassembly. Yes, it happens that the same peephole optimizer is used over the module code in the same way it's used over a function body, but that certainly doesn't have to be the case. And the answer also hasn't really explained why the same optimization is not done directly at the REPL.
– wim
Mar 26 at 15:51
@wim I certainly delete the answer if you don't think it meets the mark. I personally don't feel the tweaking was negative, but I hear you--it's not exactly the same. But I think all of this is pretty tied to what Python does today and isn't a guarantee of what Python may do in the future. And that's a fair criticism--I did not explicitly state why line by line at the REPL is different.
– John Szakmeister
Mar 26 at 16:55
Oh, I don't think you should delete it, just update it! I do think the disassembly demonstration is useful content. However, it shoulddis
module scoped code, not function scoped code - recent versions of dis allow you to pass in a string of source directly, so it's not necessary to use a function object's__code__
attribute.
– wim
Mar 26 at 17:16
@wim That doesn't seem very fair to you--I think you've done the work on that front already and your answer should be the accepted answer (though I think adding the interning of small integers to the answer is good, just in case people try and get results they don't expect).
– John Szakmeister
Mar 27 at 21:09
add a comment |
There are actually two things to know about CPython and its behavior here.
First, small integers in the range of [-5, 256] are interned internally.
So any value falling in that range will share the same id, even at the REPL:
>>> a = 100
>>> b = 100
>>> a is b
True
Since 300 > 256, it's not being interned:
>>> a = 300
>>> b = 300
>>> a is b
False
Second, is that in a script, literals are put into a constant section of the
compiled code. Python is smart enough to realize that since both a
and b
refer to the literal 300
and that 300
is an immutable object, it can just
go ahead and reference the same constant location. If you tweak your script
a bit and write it as:
def foo():
a = 300
b = 300
print(a==b)
print(a is b)
print("id(a) = %d, id(b) = %d" % (id(a), id(b)))
import dis
dis.disassemble(foo.__code__)
The beginning part of the output looks like this:
2 0 LOAD_CONST 1 (300)
2 STORE_FAST 0 (a)
3 4 LOAD_CONST 1 (300)
6 STORE_FAST 1 (b)
...
As you can see, CPython is loading the a
and b
using the same constant slot.
This means that a
and b
are now referring to the same object (because they
reference the same slot) and that is why a is b
is True
in the script but
not at the REPL.
You can see this behavior in the REPL too, if you wrap your statements in a function:
>>> import dis
>>> def foo():
... a = 300
... b = 300
... print(a==b)
... print(a is b)
... print("id(a) = %d, id(b) = %d" % (id(a), id(b)))
...
>>> foo()
True
True
id(a) = 4369383056, id(b) = 4369383056
>>> dis.disassemble(foo.__code__)
2 0 LOAD_CONST 1 (300)
2 STORE_FAST 0 (a)
3 4 LOAD_CONST 1 (300)
6 STORE_FAST 1 (b)
# snipped...
Bottom line: while CPython makes these optimizations at times, you shouldn't really count on it--it's really an implementation detail, and one that they've changed over time (CPython used to only do this for integers up to 100, for example). If you're comparing numbers, use ==
. :-)
There are actually two things to know about CPython and its behavior here.
First, small integers in the range of [-5, 256] are interned internally.
So any value falling in that range will share the same id, even at the REPL:
>>> a = 100
>>> b = 100
>>> a is b
True
Since 300 > 256, it's not being interned:
>>> a = 300
>>> b = 300
>>> a is b
False
Second, is that in a script, literals are put into a constant section of the
compiled code. Python is smart enough to realize that since both a
and b
refer to the literal 300
and that 300
is an immutable object, it can just
go ahead and reference the same constant location. If you tweak your script
a bit and write it as:
def foo():
a = 300
b = 300
print(a==b)
print(a is b)
print("id(a) = %d, id(b) = %d" % (id(a), id(b)))
import dis
dis.disassemble(foo.__code__)
The beginning part of the output looks like this:
2 0 LOAD_CONST 1 (300)
2 STORE_FAST 0 (a)
3 4 LOAD_CONST 1 (300)
6 STORE_FAST 1 (b)
...
As you can see, CPython is loading the a
and b
using the same constant slot.
This means that a
and b
are now referring to the same object (because they
reference the same slot) and that is why a is b
is True
in the script but
not at the REPL.
You can see this behavior in the REPL too, if you wrap your statements in a function:
>>> import dis
>>> def foo():
... a = 300
... b = 300
... print(a==b)
... print(a is b)
... print("id(a) = %d, id(b) = %d" % (id(a), id(b)))
...
>>> foo()
True
True
id(a) = 4369383056, id(b) = 4369383056
>>> dis.disassemble(foo.__code__)
2 0 LOAD_CONST 1 (300)
2 STORE_FAST 0 (a)
3 4 LOAD_CONST 1 (300)
6 STORE_FAST 1 (b)
# snipped...
Bottom line: while CPython makes these optimizations at times, you shouldn't really count on it--it's really an implementation detail, and one that they've changed over time (CPython used to only do this for integers up to 100, for example). If you're comparing numbers, use ==
. :-)
edited Mar 25 at 23:50
answered Mar 25 at 23:44
John SzakmeisterJohn Szakmeister
29.9k7 gold badges64 silver badges65 bronze badges
29.9k7 gold badges64 silver badges65 bronze badges
This answer leaves a lot to be desired: by "tweaking the script" you're putting the code into a function scope (local vars) instead of a module scope (global vars), which - in terms of execution and name resolution - is now a completely different scoping situation. I think that somewhat invalidates the disassembly. Yes, it happens that the same peephole optimizer is used over the module code in the same way it's used over a function body, but that certainly doesn't have to be the case. And the answer also hasn't really explained why the same optimization is not done directly at the REPL.
– wim
Mar 26 at 15:51
@wim I certainly delete the answer if you don't think it meets the mark. I personally don't feel the tweaking was negative, but I hear you--it's not exactly the same. But I think all of this is pretty tied to what Python does today and isn't a guarantee of what Python may do in the future. And that's a fair criticism--I did not explicitly state why line by line at the REPL is different.
– John Szakmeister
Mar 26 at 16:55
Oh, I don't think you should delete it, just update it! I do think the disassembly demonstration is useful content. However, it shoulddis
module scoped code, not function scoped code - recent versions of dis allow you to pass in a string of source directly, so it's not necessary to use a function object's__code__
attribute.
– wim
Mar 26 at 17:16
@wim That doesn't seem very fair to you--I think you've done the work on that front already and your answer should be the accepted answer (though I think adding the interning of small integers to the answer is good, just in case people try and get results they don't expect).
– John Szakmeister
Mar 27 at 21:09
add a comment |
This answer leaves a lot to be desired: by "tweaking the script" you're putting the code into a function scope (local vars) instead of a module scope (global vars), which - in terms of execution and name resolution - is now a completely different scoping situation. I think that somewhat invalidates the disassembly. Yes, it happens that the same peephole optimizer is used over the module code in the same way it's used over a function body, but that certainly doesn't have to be the case. And the answer also hasn't really explained why the same optimization is not done directly at the REPL.
– wim
Mar 26 at 15:51
@wim I certainly delete the answer if you don't think it meets the mark. I personally don't feel the tweaking was negative, but I hear you--it's not exactly the same. But I think all of this is pretty tied to what Python does today and isn't a guarantee of what Python may do in the future. And that's a fair criticism--I did not explicitly state why line by line at the REPL is different.
– John Szakmeister
Mar 26 at 16:55
Oh, I don't think you should delete it, just update it! I do think the disassembly demonstration is useful content. However, it shoulddis
module scoped code, not function scoped code - recent versions of dis allow you to pass in a string of source directly, so it's not necessary to use a function object's__code__
attribute.
– wim
Mar 26 at 17:16
@wim That doesn't seem very fair to you--I think you've done the work on that front already and your answer should be the accepted answer (though I think adding the interning of small integers to the answer is good, just in case people try and get results they don't expect).
– John Szakmeister
Mar 27 at 21:09
This answer leaves a lot to be desired: by "tweaking the script" you're putting the code into a function scope (local vars) instead of a module scope (global vars), which - in terms of execution and name resolution - is now a completely different scoping situation. I think that somewhat invalidates the disassembly. Yes, it happens that the same peephole optimizer is used over the module code in the same way it's used over a function body, but that certainly doesn't have to be the case. And the answer also hasn't really explained why the same optimization is not done directly at the REPL.
– wim
Mar 26 at 15:51
This answer leaves a lot to be desired: by "tweaking the script" you're putting the code into a function scope (local vars) instead of a module scope (global vars), which - in terms of execution and name resolution - is now a completely different scoping situation. I think that somewhat invalidates the disassembly. Yes, it happens that the same peephole optimizer is used over the module code in the same way it's used over a function body, but that certainly doesn't have to be the case. And the answer also hasn't really explained why the same optimization is not done directly at the REPL.
– wim
Mar 26 at 15:51
@wim I certainly delete the answer if you don't think it meets the mark. I personally don't feel the tweaking was negative, but I hear you--it's not exactly the same. But I think all of this is pretty tied to what Python does today and isn't a guarantee of what Python may do in the future. And that's a fair criticism--I did not explicitly state why line by line at the REPL is different.
– John Szakmeister
Mar 26 at 16:55
@wim I certainly delete the answer if you don't think it meets the mark. I personally don't feel the tweaking was negative, but I hear you--it's not exactly the same. But I think all of this is pretty tied to what Python does today and isn't a guarantee of what Python may do in the future. And that's a fair criticism--I did not explicitly state why line by line at the REPL is different.
– John Szakmeister
Mar 26 at 16:55
Oh, I don't think you should delete it, just update it! I do think the disassembly demonstration is useful content. However, it should
dis
module scoped code, not function scoped code - recent versions of dis allow you to pass in a string of source directly, so it's not necessary to use a function object's __code__
attribute.– wim
Mar 26 at 17:16
Oh, I don't think you should delete it, just update it! I do think the disassembly demonstration is useful content. However, it should
dis
module scoped code, not function scoped code - recent versions of dis allow you to pass in a string of source directly, so it's not necessary to use a function object's __code__
attribute.– wim
Mar 26 at 17:16
@wim That doesn't seem very fair to you--I think you've done the work on that front already and your answer should be the accepted answer (though I think adding the interning of small integers to the answer is good, just in case people try and get results they don't expect).
– John Szakmeister
Mar 27 at 21:09
@wim That doesn't seem very fair to you--I think you've done the work on that front already and your answer should be the accepted answer (though I think adding the interning of small integers to the answer is good, just in case people try and get results they don't expect).
– John Szakmeister
Mar 27 at 21:09
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55347581%2fwhy-does-the-is-operator-behave-differently-in-a-script-vs-the-repl%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
@Aran-Fey: it's not all that important what type of object is being compared this way, but in this particular case it's because the entity that compiled the
.py
source file noticed that 300 == 300 so made only one 300 instance, while the interpreter that read the>>>
lines didn't notice that 300 == 300 so made two separate 300 instances.– torek
Mar 25 at 23:05
3
Still,
is
is for object identity. Only use it when that's what you want.– Chris
Mar 25 at 23:05
3
@Chris I don't think that really answers the question, either. Like torek said, this question is about the difference between running the same code in the REPL vs as a script.
– Aran-Fey
Mar 25 at 23:06
5
As @Chris says. In other words identity of two immutable objects with the same value is incidental and not guaranteed / required. Why it once matches and once doesn't is hence immaterial.
– Ondrej K.
Mar 25 at 23:14
1
Folks, none of those answers actually answers the question. :-(
– John Szakmeister
Mar 25 at 23:34