1

I wanted to test whether a value was either True or None and thought the pythonic way of writing this conditions was val in (True, None).

Turns out, if val = 1 this evaluates to True.

The only way I could find was the clumsy val is True or val is None.

What is the deeper reason that 1 in (True, None) evaluates to True? Is there a more compact way of writing the above condition?

ARF
  • 7,420
  • 8
  • 45
  • 72
  • This doesn't answer your question, but I suggest leaning towards designs where you don't have to distinguish between `val` being `1`, and `val` being `True`. I often find it beneficial to restrict a variable to a single type - if I only ever assign bools to it, I know it will never be 1; and if I only ever assign ints to it, I know it will never be True. There's no chance for ambiguity, that way. – Kevin Jan 28 '20 at 14:13
  • 2
    @Kevin "if I only ever assign bools to it"-- bools *are* ints in python (`isinstance(True, int)`) – Chris_Rands Jan 28 '20 at 14:18
  • @Kevin This is an interface to a c library. Hence the somewhat unusual use case. – ARF Jan 28 '20 at 14:21
  • Strange, I would expect C libraries to have _stricter_ type requirements than Python's. Unless it's returning a void pointer, I suppose. – Kevin Jan 28 '20 at 14:33
  • @Chris_Rands Please reopen. The referenced answers only answer the first part of my question. Whether the condition can be written more compactly is not. Do you want me to reformulate to limit the scope of the question to the second part? – ARF Jan 28 '20 at 14:39
  • using `is` is fine, you could alternatively use `isinstance(1, (bool, type(None)))` (also true for `False`) but i prefer using `is`-- i'll re-open anyway – Chris_Rands Jan 28 '20 at 14:45

1 Answers1

1

True is bool which is inherits from integer and is equal to 1 (but is not the exact same as 1).

help(bool)

Output:

class bool(int)
 |  bool(x) -> bool
 |
 |  Returns True when the argument x is true, False otherwise.
 |  The builtins True and False are the only two instances of the class bool.
 |  The class bool is a subclass of the class int, and cannot be subclassed.
 |
 |  Method resolution order:
 |      bool
 |      int
 |      object

But because True inherits from int, we should be able to do everything with a bool that we can with an int!

print(5 + True) # > 6
print(17 - False) # > 17
print(15//True) # > 15
print(10/False) # > raises ZeroDivisionError since False "=" 0

To answer your main question however, which is explaining why 1 in (True, None) evaluates to True is because the in operator works by checking equality (with ==) and not object identity (with is). The None in your statement has nothing to do with it; it could be any object and your statement would still evaluate to True.

print(1 == True) # > True
print(1 is True) # > False

True is 1 but with a little extra fluff added to it from the bool methods

I think a pythonic way of achieving what you're after is to check if the object's identity is contained in the list (which is what you suggested, but we can generalize it a bit)

id(1) in [id(x) for x in [True, None]] # > False

Essentially this is just a stronger version of the in operator. We can write it as a function:

def stronger_in(val, it):
    return id(val) in [id(_) for _ in it]
print(stronger_in(1, (True, None)) # > False
SyntaxVoid
  • 2,501
  • 2
  • 15
  • 23