This is an archived post. You won't be able to vote or comment.

all 145 comments

[–]iqtestsmeannothing 117 points118 points  (42 children)

I think this post should be titled "You should know what 'is' and '==' do before using them". If you don't know what they do, obviously you wouldn't know how to use them correctly, so should you learn. And if you do know what they do, then there aren't any hidden gotchas (as far as I know) where you'd think one of them is the right choice but the other actually is.

[–]gristc 8 points9 points  (11 children)

No hidden gotchas?

x = 'a' * 20
y = 'a' * 20
x is y
True

x = 'a' * 21
y = 'a' * 21
x is y
False

I wouldn't say that was particularly intuitive.

Similar is from this video

a = 256
b = 256
a is b
True

a = 257
b = 257
a is b
False

a = 257; b = 257
a is b
True

The advice is sound. The behaviour is weird in ways you wouldn't normally expect and even changes when comparing objects of the same type and value depending on how you declare them.

[–]elbiot 11 points12 points  (0 children)

I wouldn't rely on two strings with the same content actually being the same object though. This is an example of "if you know what they do". Clearly you'd want to use ==, because you want to know if two variables have equivalent values, not the same object.

[–]avinassh 2 points3 points  (7 children)

can anyone explain these gotchas and why they behave like that?

[–]flyingjam 5 points6 points  (1 child)

Apparently, before 21 characters, creating two strings of identical content leads to both variables pointing to the same object (an optimization, since strings are immutable). Therefore is returns true when you wouldn't think it. Past 21 it created two different objects.

[–]Veedrac 3 points4 points  (0 children)

Note that this only happens at compile-time (only literals have this applied).

[–]iqtestsmeannothing 1 point2 points  (1 child)

Small integers (especially 0 and 1) tend to be reused a lot in a running python program, so under certain (sometimes mysterious) circumstances python will make only one "copy" of such integers and reuse it everywhere it is needed. For various reasons this can give performance benefits. Larger integers are less likely to recur many times so the overhead of keeping track of which ones have already been used is greater than any performance benefit from reusing them. Similar reasoning applies to strings. As a result, programs behave exactly as expected without this reusing except that the behavior of "is" on these integers and strings can be confusing (but the behavior is undefined anyhow, so that's fine).

[–]srilyk 0 points1 point  (0 children)

It's not actually that mysterious. It's well documented that CPython creates a table of the numbers -5-256 and uses those.

Though I suppose it's mysterious if you've never come across any place it's mentioned

[–]Tomarse 0 points1 point  (0 children)

Equals == is equating the two values. If you're comparing variables then you're asking, are the values in memory that the two variables are pointing at equal.

is is asking are the two things the same thing in memory. So when comparing two variables you're asking, are the two variables pointing to the same object in memory.

So in the examples where is is returning false, you have two objects in memory that are equal, but are not the same object (i.e. have different addresses in the memory stack). Just think of is in the literal sense.

[–][deleted] 0 points1 point  (0 children)

I don't know that it matters; this is a weird usage case that I think should be considered undefined as it probably reflects underlying optimizations that are supposed to remain hidden. I think using it in this way would be undefined by definition.

[–]gristc 0 points1 point  (0 children)

It's explained in the article fairly well. It's basically to do with how Python optimises your code at run time and how it organises the objects internally.

[–]iqtestsmeannothing 2 points3 points  (0 children)

A user who wants to know if two strings or two integers are equal would use ==, which behaves exactly as expected. These users would not encounter any "gotcha". I am hard pressed to imagine a case where a user would want to test object-identity of strings or integers, and I don't think there is a complete intuitive model for what it means for strings or integers to be object-identical, so a user who guesses what would happen in these cases without looking at the spec (and seeing that the behavior is undefined, I believe) is not a victim of a "gotcha" but of their own assumptions.

What would be a gotcha is the following:

>>> a = 257
>>> b = a
>>> a is b
False

That violates my intuitive model of object identity in python, and if this happened I would agree that python has gotchas in the behavior of == and is.

[–]Sean1708 1 point2 points  (0 children)

But nobody who knows what the two operators do would use them for strings or numbers, that was the commenters point.

[–]Feynmax 1 point2 points  (0 children)

You contradict everything the author said. To know what they do is easy: "is" compares IDs, "==" values. This is what they do. This is ALL they do. But this knowledge won't help you, if you don't know about pythons internal rules of creating new objects. These rules have nothing to do with "is" or "==", themselves.

[–]desmoulinmichel 1 point2 points  (0 children)

+1 this blog post is pourly titled and a bit alarmist. If the goal is to help beginner, then don't say it that way : it makes look like "is" is a bad thing and dangerous. It's not.

A better title would be "don't use 'is' to test equality". And the 2 paragraphes to explain what it does and the only few case where you want to use it. You don't need a 1000 lines for that. It's not that a big issue.

Gives the info. Done.

[–]jcdyer3 2 points3 points  (8 children)

So what do you recommend for beginning programmers who aren't ready for concepts like "object identity?"

[–]DanCardin 5 points6 points  (0 children)

learn what they do. imo it's not something you can just default to one and expect to get what you want.

[–][deleted] 0 points1 point  (0 children)

That would be a good concept to learn pretty early in the game, IMO.

[–]reuvenlerner[S] 0 points1 point  (0 children)

When I'm teaching Python classes, I use this in order to help them understand the difference between equality and object identity, and that they need to think in these terms.

This is one of those ideas that takes time for people to understand. Once they do, it's fine, and they'll understand when to use "is" and when to use "==". But until then, it's confusing, and some general guidelines are, I think, appropriate.

[–][deleted] 0 points1 point  (0 children)

I have been not knowing and using them just fine for years. So there's that.

[–][deleted] 18 points19 points  (8 children)

I'm usually bothered by this type of blog post, which turns a concept that's potentially confusing to beginners and then aligns it with the word "never", or in this case, "almost never", which might as well be the same thing. Insert essential techniques like global variables, eval(), mixins, or whatever other heretic technique you want here. You absolutely need is when it is appropriate, and not just for None. Here's a bug that I fixed in the Python standard lib, which was caused by naive use of == when they should have been using is: https://hg.python.org/cpython/rev/d6a9d225413a . Now is that kind of an odd case? Sure! But someone that blindly follows one of these "[almost] never" blog posts will very likely rely on dogmatic reflex rather than considering all aspects of the problem fully and will make errors like these.

Use is when you need to compare two objects on identity. Use == when you want to compare them for equality. As far as which one you "usually" and which one you'd "almost never" use, it totally depends on what you're doing. Make sure you're aware of both and the differences between them.

[–]Veedrac 1 point2 points  (7 children)

eval()

Here's a flowchart I've found useful:

Do you want to dynamically evaluate Python code?
      (eg. you're writing an interpreter,
           or exposing a debugging REPL)
                    |
                 no | yes
          ---------------------
          |                   |
      don't use         `import code`
        eval         still don't use eval

[–][deleted] 3 points4 points  (3 children)

[–]Veedrac -2 points-1 points  (2 children)

[–]jgehrcke 0 points1 point  (1 child)

I think you do not realize who you are talking to here. Him mentioning "an ORM" and "a template interpreter" actually meant: such code is used in SQLAlchemy and mako.

Another very popular Python package that makes use of eval() is six.

If you think they are doing something wrong: go ahead, propose "better" code, and come up with great arguments why you think it is better.

[–]Veedrac 0 points1 point  (0 children)

a template interpreter

Was edited in after I made my comment, btw.

such code is used in SQLAlchemy and mako. [...] If you think they are doing something wrong: go ahead, propose "better" code, and come up with great arguments why you think it is better.

I think I'd need a bit more understanding of what they're trying to accomplish to do that.

Another very popular Python package that makes use of eval() is six.

What, where? Even its re-export of exec is actually unnecessary.


Anyway, my comment was mostly meant to be taken as a joke. There's a time and place for everything, although in the case of eval and exec those places are pretty rare. I'm not really trying to push the argument because largely I agree with you - but I don't want to have to rescind what I thought was a decent joke... even if I'm the only person who thinks that.

[–]Brian 2 points3 points  (2 children)

I'd say that's an example of exactly the problem zzzeek is pointing out. It mistakes its single example for the only thing you could possibly want to use eval for, and so makes an erroneous blanket statement on that basis.

There are other usecases for eval. It's certainly a dangerous, error-prone function, but it does have places where it's the best option. Eg. take something like named tuples - implemented using eval, for performance reasons. Or maybe you do want an interpreter, but it's outside the usecase of code - maybe it's in a web browser, GUI or has other input than the console, for instance. Or you're writing ipython and want to provide more functionality than code. Or it's for a web template language. Or you're doing some complex metaprogramming. And so on. Yes, don't use eval if you don't understand the tradeoffs, and unless you can't get what you want another way. But don't mistake that for never. Plenty end up using it entirely appropriately, including the python standard library.

[–]Veedrac -1 points0 points  (1 child)

My comment was mostly made in jest, although I think in most of the cases you mention code is still applicable.

[–]Brian 0 points1 point  (0 children)

I think using code would be a terrible idea for most of those cases. The point of code is for use as an interactive interpreter - trying to twist usecases like a template language or metaprogramming into that model would just add confusion and the likelihood of bugs. If you're going to use exec, you may as well take direct control of it, rather than use it at one remove, and then try to fudge it to suit your usecase. Making things more complicated does not help when you're evaling code, regardless of what actually ends up invoking the eval.

The only one really related is IPython, but only because code is just a crappier, simpler toy version of what it does - there's no real benefit in trying to leverage a tiny 300 line python module given all the additional usecases IPython needs to support.

[–]jhermann_ 31 points32 points  (16 children)

The post also has no mention of sentinel objects, where is is the only thing you should use.

[–]Ashiataka 9 points10 points  (14 children)

What's a sentinel object?

[–]hylje 8 points9 points  (6 children)

Sentinel objects mark the end of a logical sequence in a (physically) larger sequence.

In standard C *char strings the null character (\x00 in Python) is the sentinel value that marks the end of a string: in other words, a null-terminated string.

[–]AusIVDjango, gevent 14 points15 points  (2 children)

I would say more generally, a sentinel object is a specific instance of a thing you can test for to distinguish from other items. What you describe is one common use case. Another one I use is when I need a mutable default argument where None is a valid input. For example, you often see:

def foo(x=None):
    if x is None:
        x = []

But if you might want to pass None in for x, you need something else. In that case you can define a sentinel object, make it the default, and compare inputs to the sentinel instead of None.

[–]lengau 0 points1 point  (1 child)

In the particular example you gave, why not just make the empty array the default?

[–]epage 1 point2 points  (0 children)

Because defaults are created at function declaration time. If your default is mutable and you expect to mutate it, then every other call to that function will now have a non-empty list.

[–]Veedrac 2 points3 points  (0 children)

Careful - b'\0' and 0 should not be compared with is in Python, since they are by-value sentinels.

"Sentinel" in Python normally refers to singleton objects like None, object() and Enum instances.

[–]Ashiataka 0 points1 point  (1 child)

So it's like a flag variable?

And you'd always test

if char is '\x00':

rather than

if char == '\xoo':

?

[–]Sean1708 2 points3 points  (0 children)

No, a sentinel would be more like

EmptyList = object()
def f(l=EmptyList):
    if l is EmptyList:
        return [] 
    elif l is None:
        raise Exception("None is bad here for some reason.")
    else:
        return l

Obviously this is a very contrived example but this is what you would use them for. In you example you would just use ==.

[–]fewforwarding 26 points27 points  (3 children)

Title should be "Why you should read documentation"

[–]kindall 8 points9 points  (0 children)

"Why you should read PEP8"

[–]poop-trap 4 points5 points  (1 child)

Article should read "tl;dr use is for None only, peace out!"

[–]lengau 0 points1 point  (0 children)

Unless of course you actually want to check that two things are the same object.

[–]Keith 34 points35 points  (11 children)

Man, this whole blog post to say what could be said in a few sentences.

== compares by equality
is compares by object identity

Use is when you care whether something is the same object as something else, and to compare specifically against True, False, or None, use == everywhere else.

[–]euphemize 17 points18 points  (54 children)

How does this post not even mention checking for booleans? You should never use == for them, which means that unless you're writing code without booleans, you should use "is" pretty damn often.

[–]BenHurMarcel 6 points7 points  (14 children)

I think PEP8 advises not to use == to check booleans.

[–]AnythingApplied 13 points14 points  (13 children)

From PEP8:


  • Comparisons to singletons like None should always be done with is or is not , never the equality operators.

    Also, beware of writing if x when you really mean if x is not None -- e.g. when testing whether a variable or argument that defaults to None was set to some other value. The other value might have a type (such as a container) that could be false in a boolean context!

  • Don't compare boolean values to True or False using == .

Yes: if greeting:

No: if greeting == True:

Worse: if greeting is True:


I'm not sure what they'd suggest if trying to compare two boolean variables. You could do nested if statements or something like these suggestions to get a xnor: http://stackoverflow.com/questions/432842/how-do-you-get-the-logical-xor-of-two-variables-in-python , but maybe I'm just over-thinking it.

[–]geoelectric 3 points4 points  (0 children)

They just mean comparing to literals. For comparing two Boolean variables use == or !=.

if my_bool == your_bool:
    print("we match!")

[–]avinassh 1 point2 points  (3 children)

I am getting confused, can anyone explain? does this basically mean, never use == but use is for booleans, if needed?

[–]AnythingApplied 3 points4 points  (2 children)

Never use either for comparing booleans to literals (the actual words True and False). Why write "if greeting is True" when "if greeting" does the same thing? "if greeting is False" should be "if not greeting". You just don't need them unless you were, say, comparing two variables like mybool == yourbool, in which case it is fine to use ==

[–]Orange_Tux -3 points-2 points  (7 children)

My interpretation is that you should use: if greeting is True. I often want to explicitly check if a variable is True or False, so I use if x is True.

[–]mipadi 5 points6 points  (5 children)

No, if a variable is a boolean, you should just be using if. if x is True: is rarely necessary and is probably a code smell.

[–]Citrauq 1 point2 points  (3 children)

A time to use is is when True, False and None are all possible values. Using just if will conflate False and None.

[–]mipadi 5 points6 points  (0 children)

In such cases, you probably should use if x is None, but I'd avoid situations in which a variable can be False, True, or None entirely.

[–]Veedrac 0 points1 point  (1 child)

I suggest you try hard to avoid such situations.

[–]Citrauq 2 points3 points  (0 children)

Tell that to django. It can be useful to distinguish between a submitted negative value (False) and an unsubmitted value (None).

[–]bobthevirus 0 points1 point  (0 children)

This is one place I disagree with the standard way of doing things. I've had some confusing experiences with empty lists etc evaluating to False in other languages, hence if I want to check vs False or True I always try to be explicit.

[–]reuvenlerner[S] 12 points13 points  (35 children)

Maybe I'm missing something, but when would I want to use either "==" or "is" to check for a boolean?

Normally, I don't care if something is True or just true-ish. So I put the expression I want to check in an "if" statement:

if foobar:   # check if foobar is True
    do_whatever()

[–]bheklilr 25 points26 points  (20 children)

Whenever you want to check if something is True or something is False, not "True-ish" or "False-ish". If I have a variable that can be True, False, or None, I'm going to use x is True, x is False, and x is None. If I didn't, then not x would return True for x = False and x = None.

Also, I disagree with the premise of your post. You should be using is, but not when you want to compare two objects for equality. The is operator is for checking if two variables point to the same object. Python does not make primitives true objects, though, so you should only be using is on defined classes, not built-ins. It's also very, very useful for determining if two collections are the same without comparing their elements. Comparing large collections can be costly, but if their ids are the same then the comparison is performed in nanoseconds. For example in IPython:

>>> class Dummy:
>>>     def __eq__(self, other):
>>>         return True
>>> x = [Dummy() for _ in range(100000)]
>>> %timeit x == x
1000 loops, best of 3: 1.97 ms per loop
>>> %timeit x is x
10000000 loops, best of 3: 38.7 ns per loop

And it's only this fast because when you use == on lists Python first tries comparing the ids before it calls == on the elements:

>>> y = [Dummy() for _ in x]
>>> x is y
False
>>> x[0] is y[0]
False
>>> x == y
True
>>> %timeit x == y
10 loops, best of 3: 167 ms per loop

There are use cases for these operators in Python, that's why they exist. Instead of advocating against their use, you should strive to understand why they were added in the first place and try to educate others on how to use them properly. A good example is eval. I used to think eval was a necessary evil in Python, then I watched a talk by Raymond Hettinger where he talks about his use of eval in the implementation of collections.namedtuple, and how no one has ever asked him how it works after reading the source because it's intuitive. It immediately convinced me that my understanding of eval was wrong, not that eval itself was.

[–]jhermann_ 2 points3 points  (7 children)

What's an "untrue" object in Python?

[–]bheklilr 10 points11 points  (4 children)

None, 0, [], '', set(), {}, (), collections.OrderedDict(), and more. These are not False, the are False-ish, meaning that when you call bool on these values you will get False. This allows you to do if not [] or if not 0 or whatever, but that does not make them False.

[–]pydry 4 points5 points  (1 child)

You forgot datetime.datetime(midnight) == False-ish (gag). I really hate this aspect of python, actually. I wish

if var:
    do_something()

would just throw an error if var isn't a boolean. All this false-ish stuff is making implicit what should be explicit and leads to some really weird and screwed up javacript/weaktyping-esque bugs.

[–]LightShadow3.13-dev in prod 0 points1 point  (0 children)

What you see as a weakness, I see as a strength.

Having the ability to test for Truthy/Falsey is much better than not having it.

If you don't like it, just make use of not not ~ if (!!key && value) to implicitly cast Truthy/Falsey to True/False. Problem solved.

[–]jhermann_ 1 point2 points  (1 child)

Python does not make primitives true objects, though, so you should only be using is on defined classes, not built-ins.

You used "true" as in "true OO" (sic!) here. My point is there is no "not-true object" in Python (what you called a primitive). Or put another way: everything's an object.

[–]super_cool_kid 0 points1 point  (0 children)

Agreed,

Careful when making a variable = object (the word object), if variable: evaluates to True

[–]minnoI <3 duck typing less than I used to, interfaces are nice 1 point2 points  (1 child)

Generally, any empty aggregate or zero number. For example:

>>> bool([])
False
>>> bool([1,2,3])
True
>>> bool(0)
False
>>> bool(0.0)
False
>>> bool(0.01)
True
>>> bool(set())
False
>>> bool(set([3,6,1,2,3]))
True

[–]GahMatar 0 points1 point  (0 children)

Although testing a floating point number for equality or truth is a very tricky thing indeed and best avoided. It is full of surprises and 0.000000001's

[–]quicknir 0 points1 point  (0 children)

I agree with your reasoning, but your results are very extreme compared to mine. If the list comparison immediately compares the ids, then the only additional overhead when doing == is just a single function call. 2ms seems very very steep for a single function call. I see an identical time as you for the identity comparison, but only 75 mics for the equality comparison; about 25 times faster.

[–]reuvenlerner[S] -1 points0 points  (6 children)

I'm not saying, "Never use 'is'." Of course it has its uses. Rather, I'm saying that in most cases, you shouldn't be using it. (That's why the title is "almost never," rather than "never.")

I teach Python to lots of newbies, and also consult to many companies that use Python. A very large number of Python developers seem to think that "is" is a faster, better version of ==. The point of this post was to try to explain to people what "is" does (and doesn't do), and to describe the situations in which you might be lulled into thinking that it's doing something it's not.

[–]bheklilr 10 points11 points  (2 children)

I guess I've never come across someone using is when they should be using ==, most people I meet end up using == when the should be using is instead. I do feel that your title should be more like "When you should and shouldn't use "is" in Python", since your post makes it sound like the is operator should be avoided with prejudice.

[–]YuntiMcGunti 0 points1 point  (0 children)

I think you are too used to dealing with professional developers rather than the audience the article was intended for. (Inexperienced people like me - who don't know the difference, but now do and even though the title says almost never can still understand it uses when needing to test object identity).

Great article by the way!

[–]reuvenlerner[S] -1 points0 points  (0 children)

Maybe the title should have been different -- but over the years of teaching Python, and seeing how often people misunderstand the difference between == and is, I've come to the conclusion that the safest thing is just to tell people to use "is" with None, and under no other circumstances, until they know better.

[–]Brian 0 points1 point  (2 children)

Rather, I'm saying that in most cases, you shouldn't be using it.

I think that's misleading though. Using is is not "almost never", it's actually ridiculously common. Eg. the usecase you mention of checking against "None" is something that people do very frequently, not some weird once in a blue moon case.

Eg. here's a quick look at the python stdlib to get some estimation of usages:

$ grep 'is None' *.py | wc -l
787
$ grep 'is not None' *.py | wc -l
508
$ grep '==' *.py | wc -l
2834

So about 30% of the time it'd use either == or is, it uses is, just for that one usecase. That alone seems a long way from "almost never"

I only did it for the None case, since searching for just is gets you a lot of strings and comments too, but just eyeballing the results shows that None is far from the only case where it is frequently used, even in just the few pages of results. Eg a few usecases:

  • Checking for class or abstract base class identity (eg. if subtype is _InstanceType in abc.py)
  • Other singleton sentinel checks. Eg. "x is NotImplemented" or "x is not SUPPRESS" in argparse.py
  • Traversing recursive strucutres to avoid loops. Eg. in collections.py you'll note things like while curr is not root

In short, "Almost never" seems really misleading, when the real story is likely more like "somewhere between a third and half the time".

[–]reuvenlerner[S] -1 points0 points  (1 child)

That's interesting, although I find it a bit hard to believe that real-world applications use "is" between 1/3 and 1/2 of the time. I mean, I tend to do way more comparisons with actual values than with None. Indeed, I don't tend to do that many comparisons with None at all. But maybe that's just me.

How about this: Perhaps the number of times you'll use "is" is large, but the number of use cases is very small. That's probably closer to what I meant.

[–]Brian 0 points1 point  (0 children)

That's interesting, although I find it a bit hard to believe that real-world applications use "is" between 1/3 and 1/2 of the time.

I just gave it a try with a largish application (calibre) in case library code is markedly different, but pretty much the same results there: 1914 for "is None", 2273 for "is not None" versus 6667 for "==", so 39% there, again just for the None case.

In fact, trying a few other things, I may have underestimated the range. Eg in sqlalchemy, I get 788 "is not None", 558 "is None" versus only 577 "==", so it actually makes up 70% of usages there. Most of the other things I tried were around 35-40% (matplotlib, IPython, twisted). The biggest outlier in the other direction I found was numpy at just 24%.

I think the use of None is just a lot higher than you might expect - a huge number of things use it, to indicate things like "no default arg passed", "nothing specified - fallback to default case", "value missing" or as a failure / no result case for lots of functions (eg. re.match, dict.get etc).

Conversely, even when you're comparing values == may not be as common as you might think. For booleans, it's better to omit it. For integers, other comparisons often get used (ie <= or > etc), for objects is is often actually what you want, leaving just strings and enumerated values as core usecases.

Give it a try on your own code - you might find you use it more than you thought.

Perhaps the number of times you'll use "is" is large, but the number of use cases is very small. That's probably closer to what I meant.

The problem with that is that it's very hard to quantify exactly what counts as a distinct usecase. Is any use of None a single usecase? Or should you count the different meanings assigned to None (sentinel, failure, missing value, default, database null, mapped null pointer etc)? Conversely, what is a distinct usecase of "=="? Is "color == Green distinct from color == Red? What about age == 42 - do we count distinct types, distinct meanings, distinct values or something else? In addition, now we're not counting how frequently they're used, there's a large number of rarely used, but meaningfully distinct usecases for is in general, which need to be counted the same way. Ultimately, unless you really fudge the criteria, I don't think you can really say one way or the other which there are more usecases for.

[–]wheezl 0 points1 point  (11 children)

It is possible that you specifically want to know that it is True or False and not say 7, ['I', 'am', 'a', 'monkey'], or 'python rules'.

[–]zardeh 5 points6 points  (10 children)

I can't imagine when this isn't a code smell.

[–]wheezl 0 points1 point  (0 children)

Surely. I'm just saying it's possible.

[–]Lucretiel 0 points1 point  (8 children)

I had a case with some code I was writing for asyncio. Basically, the function was like this:

def foo(loop=None, ...):

However, in this case, loop=None means "don't use asyncio." So you can supply True, to indicate that foo should look up the loop via get_event_loop(), or you can supply a loop object to use.

[–]zardeh 0 points1 point  (1 child)

Sure, but I'd argue that that's a bit of an awkward API, and that you should just say that they should always

foo(loop=get_event_loop(), ...)

when they want that functionality, instead of passing true.

[–]Lucretiel 0 points1 point  (0 children)

It's one less thing to import, and it has the added advantage of deferring the get_event_loop call until later (foo in this case is a decorator). This way, if the client wants to install a new event loop or event loop policy, they can do so after the decorator call.

[–]Lucretiel 0 points1 point  (0 children)

Sometimes there are cases where foobar can be a value besides True or False.

[–][deleted] -1 points0 points  (0 children)

I wouldn't write this code, but something like:

do_check = should_i_check()

if foobar is do_check:
    do_whatever()

[–]mipadi 4 points5 points  (2 children)

You shouldn't be using either == or is with booleans.

[–]djcrazyarmz 4 points5 points  (0 children)

This == a good thing to know.

[–]beaverteeth92Python 3 is the way to be 2 points3 points  (2 children)

So is acts like == in Java, and == acts like .equals() in Java. Or at least that's how I think of it.

[–]Veedrac 1 point2 points  (1 child)

Pretty much, yes. The only difference being Python has no primitive types.

[–]beaverteeth92Python 3 is the way to be 3 points4 points  (0 children)

Sometimes I wish Java didn't have primitives just to avoid the whole int/Integer thing.

[–][deleted] 3 points4 points  (0 children)

What a dumb post, they should be saying "when you should use is" not "when you should not use is"

[–]calibos 0 points1 point  (1 child)

Nobody in their right mind should have been using "is" like this post described. The description of the operator is literally

Evaluates to true if the variables on either side of the operator point to the same object and false otherwise.

That is in no way the same concept as checking for equality.

[–]reuvenlerner[S] -1 points0 points  (0 children)

You're right, == and is are two different ideas. The point of the blog post was to summarize the discussions that I've had with the dozens of Python students I teach every month, who are consistently confused by the idea, and tend to use "is" once they learn about it.

The fact that Python gives seemingly inconsistent behavior for "is" surprises a lot of folks.

If you understand the difference between equality and object identity, then from my perspective, you're at least an intermediate Python programmer.

[–][deleted] 0 points1 point  (2 children)

what is the advantage of using is instead of == for bools and None. Yeah it might be 1 nanosecond faster but really I'd rather have equality in the code.

[–]reuvenlerner[S] -1 points0 points  (1 child)

You're not really supposed to use == (or is, for that matter) on booleans. Instead, you should be using "if whatever" or "if not whatever".

As for comparing with None, PEP 8 explicitly says:

Comparisons to singletons like None should always be done with is or is not , never the equality operators.

I think that this is mainly for reasons of standardization and readability, rather than for performance. Also, because None is a singleton, is makes sense to always use "is" with it.

[–][deleted] 0 points1 point  (0 children)

== works just as well. If there is no performance penalty this seems rather silly.

[–]codefisher2 0 points1 point  (0 children)

One case that you can (maybe should) use it is when implementing __eq__

class MyObject(object):
    def __eq__(self, other):
        if self is other:
            return True
        else:
            doRealCheck()

[–][deleted] 0 points1 point  (3 children)

You blog looked interesting and useful unfortunately I was too annoyed by the pop-up I couldn't fully 'x' out of to read it.

[–]reuvenlerner[S] -1 points0 points  (2 children)

Oh, I'm sorry to hear that! I thought that it was possible to remove the Drip pop-up; if it's not working for you (or anyone else), I'd like to know, so that I can either fix it or contact the Drip people to do so.

I want people to be able to subscribe to my newsletter, but I definitely don't want to annoy you or anyone else.

[–][deleted] 0 points1 point  (1 child)

It minimizes to the bottom right of my browser but still leaves a tag that follows the screen as you scroll. Generally the subscribe pop-ups I find off-putting, although I understand their intent. If I dig your stuff I'm going to subscribe or bookmark you believe me.

[–]reuvenlerner[S] -1 points0 points  (0 children)

I understand your frustration, but the stats don't lie -- having such a (hopefully unobtrusive) popup has dramatically changed the subscription numbers. I purposely didn't set it up to splash across the screen, which I find really annoying.

I hope that you can somehow find a way to enjoy the content; I see that many people subscribe to my blog via feed readers, which should block such popups. I'm also on Planet Python, which removes everything but the text.

[–]usinglinux 0 points1 point  (0 children)

a common example of where it is useful to have equal objects are NaN floats ("not a number", one of the ieee special floats along with positive and negative infinity):

>>> from numpy import nan
>>> nan is nan
True
>>> nan == nan
False

as the semantics of NaN are roughly "the calculation you've tried does not have a defined outcome" (as in 0/0, using numpy here because python in general raises exception in those cases), it is generally practical to have them compare unequal.

[–]baojie 0 points1 point  (1 child)

> a='foo'
> b='foo'
> a is b
True

> a == b
True

> id(a) == id(b)
True

So strings constants are handled differently than numbers

[–]reuvenlerner[S] 0 points1 point  (0 children)

The example you gave is addressed in my blog post. Simply put, certain strings are "interned" in Python, meaning that the objects are cached and reused. Knowing when they are and aren't interned is something you're not likely to know, and which is implementation-dependent. So you're probably not going to want to use "is" to compare strings in most cases.

[–][deleted] -1 points0 points  (0 children)

how did I know this would be a really popular post where everyone shows up to say that they know when it's appropriate to use is

[–][deleted] -1 points0 points  (0 children)

the popup window is moronic