Difference between 'is' and '=='

D

Duncan Booth

Joel said:
It basically boils down to "don't ever use 'is' unless pushed into a
corner, and nevermind what PEP8 says about it".

A quick grep[*] of the Python library shows the following common use-cases
for 'is'. The library isn't usually a good indicator of current style
though: a lot of it was forced to do things differently at the time when it
was written. (Also, my search includes site-packages so this isn't all
standard lib: I found 5091 uses of the keyword 'is').

Comparison against None '<expr> is None' or '<expr> is not None' are by far
the commonest.

Comparison against specific types or classes are the next most common (in
the standard library '<expr> is type([])' although if the code were
rewritten today and didn't need backward compatability it could just do
'<expr> is list'.

Comparison against sentinal or marker objects e.g. in the standard library
this is usually a constant set to [].

Other singletons e.g. NotImplemented

code.py has:
if filename and type is SyntaxError:
which might be better style if it used 'issubclass' rather than 'is'.

cookielib.py has:
if port is None and port_specified is True:
naughty.

difflib.py uses:
if a is self.a:
return
in SequenceMatcher to avoid recomputing related values when changing one or
other of the sequences.

doctest does some fairly advanced identity testing. It also has:

SUCCESS, FAILURE, BOOM = range(3) # `outcome` state
...
if outcome is SUCCESS:
...
elif outcome is FAILURE:
...
elif outcome is BOOM:

fnmatch.py uses 'is' on some module names to optimise out a function call.

optparse.py uses 'is' to test for non-default options, but it has some
pretty dubious ways of generating the values it tests against:
NO_DEFAULT = ("NO", "DEFAULT")
SUPPRESS_HELP = "SUPPRESS"+"HELP"
SUPPRESS_USAGE = "SUPPRESS"+"USAGE"
....
if default_value is NO_DEFAULT or default_value is None:
default_value = self.NO_DEFAULT_VALUE
....
if not option.help is SUPPRESS_HELP:
....
elif usage is SUPPRESS_USAGE:


sre defines a bunch of constants strings like an enumeration and uses 'is'
to test them.

threading.py does identity checking on threads:
me = currentThread()
if self.__owner is me:


Non standard library:

Spambayes has:
if val is True:
val = "Yes"
elif val is False:
val = "No"
when displaying configuration options to the user.

zsi does things like:
if item.isSimple() is True:
if item.content.isRestriction() is True:
self.content = RestrictionContainer()
elif item.content.isUnion() is True:
self.content = UnionContainer()
elif item.content.isList() is True:
self.content = ListContainer()
ick.

[*] I discovered a neat feature I didn't know my editor had: grepping for
"<[c:python-keyword>is" finds all occurences of the keyword 'is' while
ignoring it everywhere it isn't a keyword.
 
J

Joel Hedlund

[*] I discovered a neat feature I didn't know my editor had: grepping for
"<[c:python-keyword>is"

Neat indeed. Which editor is that?

Thanks for a quick and comprehensive answer, btw.

Cheers!
/Joel
 
A

Adam DePrince

The Python specification allows but does not require such behind-the-scenes
implementation optimization hacks. As released, CPython 2.4 caches -5 to
99, I believe. In 2.5, the upper limit was increased to 256. The limits
are in a pair of #define statements in the int object source file. Anyone
who compiles from source can adjust as desired (though the corresponding
test will fail unless also adjusted ;-).

I think the visibility of this implementation detail from Python code is an
example of a leaky abstraction. For more, see
http://www.joelonsoftware.com/articles/LeakyAbstractions.html

I disagree wholeheartedly with this. == and is are two very different
operators that have very different meaning. It just happens that the
logical operation

(a is b ) -> (a == b )

is always True.

There is no abstraction going on here; a==b is not an abstract version
of a is b. They are different operations.

a == b tests if the values of objects at a and b are equal. a and b
point be the same darn object, or they might be different objects, but
the question we are asking is if they have the same value.

The is operator is different, you use it if you are interested in
introspecting the language.

Some people have noticed that 1 + 1 is 2 will return True and 1 + 100
is 101 returns False. This isn't a "leaky abstraction" unless you
wrongly consider is to be an analogy for ==.

Python has certain optimizations ... small strings and numbers are
"interned," that is canonical copies are maintained and efforts to
create fresh objects result in the old cached copies being returned,
albeit with higher reference counts. This saves memory and time; time
because for any intern-able objects the truth of the first test is a
realistic possibility.

if a is b:
return True
if hash( a ) != hash( b )
return False
.... Now do proper equality testing. ...

As for "joelonsoftware's" leaky abstraction article, I respectfully
disagree. His leaky abstractions are merely inappropriate analogies.
TCP is perfectly reliable with respect to its definition of
reliability.

As for wipers abstracting away the rain ...

Well over a decade ago I recall walking to lunch with my supervisor, a
truly masterful C programmer. We worked in Manhattan, a land where two
way streets are the exception. When crossing each street he would look
the wrong way, look the correct way and then stare the wrong way again.
Upon noticing my inquisitorial expression, he answered "A good
programmer always looks both ways when crossing a one way street."

I'm uncertain that a quip about abstracting away the rain would have
prompted the same adjective "masterful" now 10+ years in the future.

- Adam DePrince
 
R

Roy Smith

Adam DePrince said:
It just happens that the
logical operation

(a is b ) -> (a == b )

is always True.

Only for small values of "always". You can always do pathological
things with operators:

class Foo:
def __eq__ (self, other):
return False

f = Foo()
print f is f
print f == f

frame:play$ ./is.py
True
False

This may even be useful. What if you were trying to emulate SQL's
NULL? NULL compares false to anything, even itself. To test for
NULLness, you have to use the special "is NULL" operator.
 
D

Dave Hansen

Only for small values of "always". You can always do pathological
things with operators:

class Foo:
def __eq__ (self, other):
return False

f = Foo()
print f is f
print f == f

frame:play$ ./is.py
True
False

This may even be useful. What if you were trying to emulate SQL's
NULL? NULL compares false to anything, even itself. To test for
NULLness, you have to use the special "is NULL" operator.

Another instance where this may be useful is IEEE-754 NaN. I don't
have fpconst to verify if that's the case, but I would expect
NaN is NaN to be true, but NaN == NaN to be false.

Regards,
-=Dave
 
J

Jon Ribbens

This may even be useful. What if you were trying to emulate SQL's
NULL? NULL compares false to anything, even itself.

Strictly speaking, comparing NULL to anything gives NULL, not False.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,291
Messages
2,571,493
Members
48,161
Latest member
WiltonMath

Latest Threads

Top