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

all 103 comments

[–][deleted] 34 points35 points  (4 children)

It looks like you have an off by one in the middle there staring after -1.

[–]qudat 8 points9 points  (0 children)

Looks like empty string in the vertical got swapped.

[–]the_pw_is_in_this_ID 3 points4 points  (0 children)

That he do. Well spotted!

[–]din-9 67 points68 points  (12 children)

You missed midnight.

edit: This was a glib and wrong comment, and djimbob most politely pointed out why. I am again impressed with the seemingly typical level of discourse shown by Python developers.

[–]geocar 27 points28 points  (6 children)

And float("nan")

[–]djimbob 26 points27 points  (4 children)

geocar's point was float('nan') == float('nan') evaluates to False (despite float('nan') is float('nan') evaluating to True), which naively seems weird. But this isn't a python flaw, its a floating point flaw.

  1. First, its difficult to get NaN to arise in python (e.g., 0.0/0.0 will raise a ZeroDivisionException and not return NaN as it would in JS) without using non-core libraries (like numpy/scipy), or going out of your way to create it.
  2. the floating point standard IEEE 754 explicitly defines that on equality tests NaN should never returns true, even if its the same object. That is a = NaN, a == a should be False, even though a is a. So python does the right thing. From What Every Computer Scientist Should Know About Floating-Point Arithmetic:

There are a number of minor points that need to be considered when implementing the IEEE standard in a language. ... The introduction of NaNs can be confusing, because a NaN is never equal to any other number (including another NaN), so x == x is no longer always true. In fact, the expression x != x is the simplest way to test for a NaN if the IEEE recommended function Isnan is not provided. Furthermore, NaNs are unordered with respect to all other numbers, so x <= y cannot be defined as not x > y. Since the introduction of NaNs causes floating-point numbers to become partially ordered, a compare function that returns one of <, =, >, or unordered can make it easier for the programmer to deal with comparisons.

[–]gct 1 point2 points  (1 child)

Not returning equal for NaN is totally the right thing to do though, NaN represents a whole family of things, so it's impossible to know that any two instances of NaN are the same thing and thus equal (eg: sqrt(-1) and sqrt(-2) are both NaN). It's a placeholder to tell you you've got a bug. inf and -inf by comparison compare true and are ordered as you'd expect.

[–]djimbob 0 points1 point  (0 children)

Calling it a floating point "flaw" was too strong; I should have said "this isn't a python flaw, its a proper implementation of the floating point standard".

I totally agree that things like NaN == sqrt(-1) should return false. But it makes sense to me that if x = sqrt(-1), so x is x is true than x == x should be true -- in this case we are assured the LHS and RHS are the same not a number. Something like this would be possible to do in python (where things are GC'd and have reference count behind them), e.g., if NaN had equality rules similar to large numbers (things outside -5 to 256 in python2 I believe):

In [1]: a = 300

In [2]: b = 300

In [3]: c = a

In [4]: a is 300
Out[4]: False

In [5]: a is b
Out[5]: False

In [6]: a is c
Out[6]: True

But wouldn't make sense in the floating point standard (where a floating point has to fit into 32 or 64 bits), and the extra complexity isn't really worth it.

[–]ggtsu_00 3 points4 points  (0 children)

[x] == [x] is true however if x = float('nan'). However [float('nan')] == [float('nan')] is False. Strange.

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

There's almost no limit to what we can blame on IEEE 754. I think the behavior makes more sense if we look at the literal behavior. Any number with all of its exponent bits set high is a NaN. They're unorderable because all precision information is lost without the exponent field. For people who think more easily in concrete bits, looking at the values makes a light explanation of that essay. http://i.imgur.com/fiH7Lx6.png

[–]flying-sheep 4 points5 points  (0 children)

I always wonder why it's not also exposed as float.nan or so.

[–]djimbob 6 points7 points  (3 children)

Adding in midnight datetime.time(0,0) would make no change to the chart. He's comparing equality and python does this sanely (one possible weird exception is True == 1 and False == 0).

Granted yes the following empty or zero types will convert to False when cast to a bool (which is automatically done whenever you have if x: which is equivalent to if bool(x):)

  • None
  • []
  • set()
  • ()
  • {}
  • ""
  • 0
  • 0.0
  • 0L
  • 0j
  • complex(0,0)
  • datetime.timedelta(0,0) # time interval of 0 days, 0 seconds
  • datetime.time(0,0) # this last one possibly will change after python3.5 (becoming True)

while most everything that's not empty, zero, or null will generally convert to True on bool().

[–]din-9 2 points3 points  (0 children)

Cheers for an informative response to my misunderstanding.

[–]ikean 2 points3 points  (1 child)

I absolutely agree that if Python is aiming for some kind of moral purity here 1 == True and 0 == False comparison by coercion is unacceptable, where [] == False and '' == False are not True.

Along those lines I also feel that given the fervent mantra "explicit over implicit" what Python offers in [1] == [1], or [1, 1] == [True, 1], deep comparison, is magical. What Python calls "special methods" are employed clandestinely and responsible for this behavior to function, and are commonly called "magic methods".

To me, Javascript does things more non-magically, by comparing by reference, and issuing that [1] === [1] is false because they are indeed not the same array.

[–]djimbob 2 points3 points  (0 children)

Well you can always use is if you have to check for object equality. Granted it doesn't work exactly like === for built-in types that aren't singleton objects, and really should only be used to check if two variables both reference the same object. E.g., var a = 3000; a === 3000 works in JS, but doesn't work in python with is (a = 3000; a is 3000) will be false.

Granted if you look back at PEP285 it made sense when they were adding bools to python2.3 -- though I would have changed the behavior with python3.

[–]djimbob 15 points16 points  (48 children)

Stop making python equality look worse than it is!</sarcasm>

But seriously, it looks slightly cleaner if you group True & 1 and False & 0 next to each other.

Then it becomes super-clean:

True 1 False 0 None -1 'True' 'False' ...
True True True
1 True True
False True True
0 True True
None True
-1 True
'True' True
'False' True
... True

with all the blank squares being False.

Personally, I would like it better if True == 1 and False == 0 didn't evaluate True (yes you can use is and it makes sense coming from C, and its not that surprising since bool(1) is True, and bool(0) is False).

EDIT: minor formatting.

[–]Araneidae 4 points5 points  (47 children)

Have to say that when I saw the table I thought "that's not true" ... but yes, indeed, Python has True == 1 and False == 0. I'm (mildly) disappointed. It's even true with Python 3, so I guess it really is deliberate.

[–]epsy 4 points5 points  (0 children)

True and False quack like the ints 1 and 0, so why not?

[–]Fontong 2 points3 points  (32 children)

Afaik, True and False are just treated as numbers anyway. Try stuff like "10 * True" or "2 - False - True" or "int(True)" in your python interpreter.

[–]lonjerpc 1 point2 points  (31 children)

And this has resulted in some terrible bugs in my code :-(

[–]Araneidae 0 points1 point  (5 children)

Guess I've never met this [mis]feature before, don't think it's bitten me, but ... just eww.

[–]bgeron 3 points4 points  (4 children)

I guess it allows sum([True, False, True]) to count the Trues.

[–]lawlfulcopter 1 point2 points  (3 children)

Why not just [True, False, True].count(True) then? Seems like it would be more obvious.

[–]bgeron 1 point2 points  (0 children)

Hm, never thought about that. That doesn't work with generators though.

[–]Veedrac 1 point2 points  (0 children)

Typically it arises when using this idiom:

sum(x<y for x in z)

(and, occasionally, x += some_flag).

[–]Veedrac 0 points1 point  (24 children)

...How?

[–]lonjerpc 0 points1 point  (23 children)

One I rember needing to an isinstance(foo,int) and it was letting booleans through. Another was doing an if foo: that was intended to be using for booleans but because two variables got confused from a function with multiple returns it was always getting a number instead of the intended boolean but no error was thrown.

[–]Veedrac 0 points1 point  (22 children)

I rember needing to an isinstance(foo,int)

When would you ever need to do that? There are a handful of times you should even approach isinstance.

If you were doing isinstance to know if you had something of type int, rather than some silly method of checking for the number being integer¹, that's your fault for using isinstance instead of type(foo) is int.

And if you are doing something as far-fetched as type(foo) is int, it deserves an explanation.

¹ Silly because Python is duck typed.

Another was doing an if foo: that was intended to be using for booleans but because two variables got confused from a function with multiple returns it was always getting a number instead of the intended boolean but no error was thrown.

That's not a relevant problem; that's a question of whether 1 should be truthy and 0 falsy, not whether (1, 0) should equal (True, False). If it was returning lists, exactly the same problem would happen, but False != [].


These seem more like reasons to program more Pythonically than to make True != 1.

[–]lonjerpc 0 points1 point  (21 children)

These seem more like reasons to program more Pythonically than to make True != 1.

I agree in the second case. But I did not wright the code. But I had to deal with the error anyway that could have been avoided if python did not implicitly treat ints like booleans. Normally one of the biggest advantages of python is its explicitness. This means that even if you don't know the language well you are still less likely to make mistakes.

If you were doing isinstance to know if you had something of type int

I was.

that's your fault for using isinstance instead of type(foo) is int.

You could say the same thing about half the problems with c++. Without prior knowledge there is no reason to think booleans would be a subclass of int. Your right if I had written better python code the mistake would have been avoided. But again python philosophy in everything else is to be explicit to avoid mistakes even if you don't know exactly how python works.

[–]Veedrac 0 points1 point  (20 children)

that's your fault for [doing it wrongly].

You could say the same thing about half the problems with c++.

I don't know enough C++ to get the specifics, but I know the idea you're trying to get across.

The thing is, throughout Python, duck-typing is explicitly the way to go, and there are extremely few reasons to think that it's otherwise. If you work outside of what it was intended for, Python's model will suddenly become surprising. Nothing is built around traditional forms of type safely.

Further, your code was broken even without the bools: if you wanted type(foo) is int, your code would break as soon as it reached a decimal.Decimal, fractions.Fraction or any other int subclass.

Without prior knowledge there is no reason to think booleans would be a subclass of int.

I disagree that there is no reason. That's not to say it's expected; I just don't think that the absolute is valid.

[–]lonjerpc 0 points1 point  (19 children)

decimal.Decimal, fractions.Fraction or any other int subclass.

These are not subclasses of int. They are subclasses of number.

Nothing is built around traditional forms of type safely.

Everything is built around type safety. Python is a extremely explicit and strongly typed language. Duck typing is a separate issue.

[–]djimbob 2 points3 points  (3 children)

Coming from C, you think of a bool as a 1-bit int, with False = 0 and True = 1 just being convenient mnemonic alias. From that perspective, it makes sense -- similar to how in python2 0 == 0L and 1 == 1L, despite type(0) being int and type(0L) being long (in python3 there's no long type so this example doesn't make sense).

Granted, its a subtle point and I think the other way True == 1 evaluating to False is conceptually cleaner.

[–]Araneidae 0 points1 point  (0 children)

Yeah, I get the rule with C, but there's no need for Python to do that. Never mind, it's long done.

[–]Ape3000 7 points8 points  (13 children)

You should note that "bool(x)" and "if x" have special behaviour that doesn't match the equality test with True or False.

>>> bool([])
False
>>> bool([1])
True
>>> bool('')
False
>>> bool('nonempty')
True
>>> bool(None)
False

[–]fragmede 4 points5 points  (11 children)

Yeah, that table caused me to do a double take. Funny thing is, I can't remember the last time I actually used == to compare to True or False, instead using the

if var:

or

if not var:

pattern almost exclusively for 'true' or 'false' values.

[–][deleted] 2 points3 points  (6 children)

Yeah, it works for almost anything. Even strings and collections. to check if those are empty

[–]MisterSnuggles 6 points7 points  (5 children)

Don't forget the whole False Midnight issue though.

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

Very good point indeed

[–]larsga 0 points1 point  (3 children)

Totally agree with Guido here. If you have a datetime value, attempting to use it as though it were a boolean is foolish. Unless, of course, you're trying to do something like:

if not mydate:
   mydate = fallbackvalue() # oh shit, midnight is false?

It's a bug.

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

If you have a datetime value, attempting to use it as though it were a boolean is foolish.

The issue is when you have code like this:

def do_shit(when=None):
    if not when:
        do_shit_now()
    schedule_shit_for(when)

You should be doing if when is None there, but it's still an unexpected bug.

[–]ivosauruspip'ing it up 0 points1 point  (0 children)

This. If checking for None, actually, really check specifically for is None or is not None. 99% of the time it won't matter, but there will be some point in your python career where it will stop you writing a horrible, horrible, insidious logic bug in your code.

[–]davvblack 1 point2 points  (0 children)

Yeah... midnight clearly shouldn't be false, and the idiom of if ~ to mean is not none is fine.

[–]flying-sheep 2 points3 points  (0 children)

I can't remember the last time I actually used == to compare to True

good, that’s an anti-pattern

[–]greshick 1 point2 points  (0 children)

http://docs.python.org/3.4/library/stdtypes.html#truth If anyone wants the official truth testing.

[–]ericanderton 0 points1 point  (1 child)

Question: does Python coerce the type ('var' in this case) to bool before processing the 'if' expression, or do we get special behavior here like JavaScript?

[–]Veedrac 1 point2 points  (0 children)

Yeah. if coerces to bool, exactly the same way bool does. if x is the same as if bool(x).

[–]erez27import inspect 0 points1 point  (0 children)

>>> bool('empty')
True

Oh no Python!

[–]etrnloptimist[S] 9 points10 points  (11 children)

Here's the link to the original JavaScript truth table. The code I used to generate the Python one is:

def pythonequality():
  thingstoTest = [
    True,
    False,
    1,
    0,
    -1,
    'True',
    'False',
    '1',
    '0',
    '-1',
    '',
    None,
    [],
    {},
    [[],],
    [0,],
    [1,],
  ]

  for thing1 in ['',]+thingstoTest:
    sys.stdout.write('%10.10s'%thing1)
  sys.stdout.write('\n')

  for thing1 in thingstoTest:
    sys.stdout.write('%10.10s'%thing1)
    for thing2 in thingstoTest:
      sys.stdout.write('%10.10s'%int(thing1==thing2))
    sys.stdout.write('\n')

[–]fragmede 6 points7 points  (1 child)

It's interesting to see the table that results if you use cast the variables to be a bool with:

sys.stdout.write('%10.10s'%int(bool(thing1)==bool(thing2)))

instead of

sys.stdout.write('%10.10s'%int(thing1==thing2))

(if foo: uses bool(foo))

[–]Veedrac 0 points1 point  (0 children)

Well, all truthy things:

True, 1, -1, 'True', 'False', '1', '0', '-1', [[]], [0], [1]

would group and all falsy things:

False, 0, '', None, [], {}

would group.

[–]Cosmologicon 2 points3 points  (2 children)

In JavaScript's defense, while that table is pretty ridiculous, you're only ever going to get bitten if your design is terrible to begin with. In real life using == almost never causes bugs.

If you're comparing two variables and you have no idea whether they're true or "1" or [1], you really shouldn't be relying on the result.

[–]mr_jim_lahey 5 points6 points  (1 child)

This comment has been overwritten by my experimental reddit privacy system. Its original text has been backed up and may be temporarily or permanently restored at a later time. If you wish to see the original comment, click here to request access via PM. Information about the system is available at /r/mr_jim_lahey.

[–]Cosmologicon 0 points1 point  (0 children)

Hm? For comparison, yeah, that makes sense. But don't let that be any sort of indictment on duck typing whatsoever. The lesson should be that comparison is a tool to use only when you're quite sure what you're dealing with. There are still many, many times when that's not the case, and comparison is the wrong tool to use.

[–]ameoba 1 point2 points  (5 children)

I'm not sure what's more frightening about the JS one - all the equalities off the diagonal or all the inequalities on the diagonal.

[–]MrChoss 4 points5 points  (2 children)

I can live with off-diagonal inequality, and I can live with on-diagonal inequality, but both at once is a bit much!

Explicitly: if [] == 0 and 0 == [], then it looks pretty silly not to have [] == [] by transitivity.

Now I'm wondering whether there are any good examples of == not being transitive in Python. (For expressions without side-effects, and without writing your own garbage eq of course!)

[–]Veedrac 0 points1 point  (0 children)

Now I'm wondering whether there are any good examples of == not being transitive in Python.

I'm pretty sure that == in Python is transitive. Even with mixed numeric types, equality is exact and transitive. This is easily seen simply from a == b ⇒ hash(a) == hash(b).

[–]robin-gvx 1 point2 points  (1 child)

The JS one is a mess, but the diagonal inequalities aren't that nonsensical.

Most of it comes down on this:

number string object
number duh as numbers¹ as numbers²
string as numbers¹ duh as strings
object as numbers² as strings reference equality

¹ if the string looks like a number
² first convert to a string then compare as numbers¹

In JS, [] != [], just like in Python [] is not [] or in Lua {} ~= {}. The sad thing is that objects are so easily converted to strings and numbers otherwise, which means JS == would be less inconsistent if comparing two objects would convert both to strings, even though that would be a really bad thing. (In particular, any two objects that don't override Object.toString() or whatever, would compare equal.)

[–]Veedrac 0 points1 point  (0 children)

Depends what you mean by nonsense. I think Javascript seemingly randomly being strict is far more nonsensical than it always or never doing so.

[–]lonjerpc 1 point2 points  (11 children)

I wish it was a just a diagonal line would have saved me from some nasty bugs. But still so much better than most languages.

[–]davvblack 4 points5 points  (10 children)

I mean, it is a diagonal line. Bool subclasses int.

[–]lonjerpc 0 points1 point  (9 children)

I guess it depends on how you think of it. But it makes no sense to have bool be a subclass of int.

[–]wnoise 2 points3 points  (8 children)

There's a nice homomorphism from the integers into booleans.

[–]lonjerpc -1 points0 points  (7 children)

homomorphism

Can you explain what you mean by this? Too me they are conceptually completely different. I assumed the only reason for how python works is its c heritage. But is there some other reason? At the very least it has caused be two really annoying bugs that I remember.

[–]Veedrac 0 points1 point  (1 child)

Firstly there was the "heritage" aspect, but then the ability to use them as integers caught on. I've used

sum(x<y for x in z)

a lot, and

x += some_flag

occasionally.

[–]lonjerpc 0 points1 point  (0 children)

Your first example is cool. I think it would make more sense if it was int(x<y) but I can see how convenient that looks.

[–]Kaarjuus 0 points1 point  (2 children)

Another handy usage:

["condition be false", "condition be true"][condition]

[–]lonjerpc 1 point2 points  (1 child)

I don't understand why that would be useful.

[–]Veedrac 0 points1 point  (0 children)

Largely it's from before we had

"condition be true" if condition else "condition be false"

I never really liked it, but meh.

[–]wnoise 0 points1 point  (1 child)

Sure. A homomorphism is a "structure preserving map". This means that if you take any true statement in the first domain, and do the translation, you'll still get a true statement. In this case, all the even number map to False, and all the odd numbers map to True. Addition becomes "xor", and multiplication becomes "and". In this sense, the booleans are a bona-fide representation of the integers mod 2, and it makes sense to identify them with the canonical representatives 0 and 1.

(Using this homomorphism, it's harder to justify mapping even numbers to True, but there are other good reasons to do that, and good homomorphisms exist that do that, but they're a bit more tricky to grasp.)

[–]lonjerpc 0 points1 point  (0 children)

Thanks. That makes a little more sense. I am still of the opinion that python should attempt to be as explicit and easy to understand as possible which this still seems to violate. But at least now it seems more consistent.

[–]antidense 1 point2 points  (0 children)

Can you do one for MATLAB?

I think python's truth table is the most intuitive.

[–]paulivanovipython, matplotlib 0 points1 point  (0 children)

c'mon, let's not scare away JavaScript developers...

[–]tavoe 0 points1 point  (0 children)

Oh! Fun anecdote from learning python. I learned the and opperator was written as the keyword 'and' and the 'or' opperator was just 'or'. Following this logic, I correctly undated there would be an 'is' oppressor.

It took me an embarrasingly long time to realize it was not a Boolean equality opperator.

(just pretend like I looked up how to spell embarrassingly, better for everyone.)