0
x = 0.0


def is_zero(x):
    return x is 0 or x is 0.0


print(f'is_zero: {x} : {is_zero(x)}')
print(f'express: {x} : {x is 0 or x is 0.0}')

Being two ways to use the same expression, I'd expect the same result. But when I run the code:

is_zero: 0.0 : False
express: 0.0 : True

Can anyone explain what is going on here? My ultimate aim here is to use the expression in a list comprehension to return a list with 0 and 0.0 removed (but not instances of False, hence using 'is' rather than '==' ). Unfortunately, the expression returns False when used in a list comprehension, just as it is when used in the return line above. And yet the expression clearly returns True so...?

bduza
  • 1
  • 1
  • In Python, [you should use `==` to compare numbers](https://stackoverflow.com/questions/2239737/is-it-better-to-use-is-or-for-number-comparison-in-python) (also, if you use Python 3.8, you'll get a warning for using `is` with a literal). However, in the specific case of checking for zero, you can also just use the fact that [0 is considered false](https://docs.python.org/3/library/stdtypes.html#truth-value-testing) and just use `not x`. As for your list comprehension, it seems likely that there is either an actual non-zero value being compared. – jirassimok Apr 29 '20 at 05:00
  • 1
    The result didn't copy in correctly: is_zero: 0.0 : False express: 0.0 : True The reason I am using IS instead of == is because items in the list are sometimes just False, so if item == 0 returns True with a value of False – bduza Apr 29 '20 at 05:00
  • And sid, that's true when using == but not when using is – bduza Apr 29 '20 at 05:02
  • If your code needs to be robust, beware the case of `-0.0`, which `is not 0.0`. I'd recommend using `isinstance(x, float) and x == 0.0` for that part of the check. Regardless, I'm getting inconsistent results when running you code. In an interactive session, I get `False` for both, but as a script or module, both come out `True`. – jirassimok Apr 29 '20 at 05:13
  • `x is not False and x == 0` That seems to work, but I'd love to know why my original expression works in one instance but not in the other. – bduza Apr 29 '20 at 05:17

1 Answers1

1

Here is a closely-related question, talking about the behavior of is 0.0 in a lambda expression. Basically, Python does not guarantee that you can do identity comparisons on floats, and CPython seems to create new floats when inside an f-string.

This question about ints is also relevant: CPython only guarantees that identity comparison works for small integers.

As you've noted in a comment, using x is not False and x == 0 (or x is not False and not x) solves your problem nicely.


If you're really only looking for 0 and 0.0, and not their equivalents in any subclasses, your best solution might be to actually use type(x) is to check if your value is really an integer/float before checking if it is zero.

So I think you probably want a function like this:

def is_zero(x):
    """Determine whether x is int or float 0.

    Returns false for subtypes of int and float.
    """
    return type(x) is int or type(x) is float and x == 0

You could also use isinstance(x, float) instead, but isinstance(x, int) will also accept booleans.

jirassimok
  • 3,850
  • 2
  • 14
  • 23