13
votes

Let's say I have a list with different values, like this:

[1,2,3,'b', None, False, True, 7.0]

I want to iterate over it and check that every element is not in list of some forbidden values. For example, this list is [0,0.0].

When I check if False in [0,0.0] I get True. I understand that python casts False to 0 here - but how I can avoid it and make this check right - that False value is not in [0,0.0]?

3
A list will be evaluated as True. Check this question - Tomás Gonzalez Dowling
You should check the values individually, your methodology will only at best show you that something failed but won't tell you what, which would be you or your users next question. - DejaVuSansMono
You probably want to rethink the decision that led to a list containing False and 0 values that need to be treated differently. - chepner
@chepner actually you're right, bit I got this issue while solving kata from codewars. :) - Paul

3 Answers

24
votes

To tell the difference between False and 0 you may use is to compare them. False is a singleton value and always refers to the same object. To compare all the items in a list to make sure they are not False, try:

all(x is not False for x in a_list)

BTW, Python doesn't cast anything here: Booleans are a subclass of integers, and False is literally equal to 0, no conversion required.

7
votes

You would want to use is instead of == when comparing.

y = 0
print y == False # True
print y is False # False

x = False
print x == False # True
print x is False # True
0
votes

Found a weird corner case on differentiating between 0 and False today. If the initial list contains the numpy version of False (numpy.bool_(False)), the is comparisons don't work, because numpy.bool_(False) is not False.

These arise all the time in comparisons that use numpy types. For example:

>>> type(numpy.array(50)<0) 
<class 'numpy.bool_'>

The easiest way would be to compare using the numpy.bool_ type: (np.array(50)<0) is (np.False_). But doing that requires a numpy dependency. The solution I came up with was to do a string comparison (working as of numpy 1.18.1):

str(numpy.bool_(False)) == str(False)

So when dealing with a list, a la @kindall it would be:

all(str(x) != str(False) for x in a_list)

Note that this test also has a problem with the string 'False'. To avoid that, you could exclude against cases where the string representation was equivalent to itself (this also dodges a numpy string array). Here's some test outputs:

>>> foo = False
>>> str(foo) != foo and str(foo) == str(False)
True

>>> foo = numpy.bool_(False)
>>> str(foo) != foo and str(foo) == str(False)
True

>>> foo = 0
>>> str(foo) != foo and str(foo) == str(False)
False

>>> foo = 'False'
>>> str(foo) != foo and str(foo) == str(False)
False

>>> foo = numpy.array('False')
>>> str(foo) != foo and str(foo) == str(False)
array(False)

I am not really an expert programmer, so there may be some limitations I've still missed, or a big reason not to do this, but it allowed me to differentiate 0 and False without needing to resort to a numpy dependency.