tests each element in turn to see if it is a true value. So it tests:
None # not a true value
None # not a true value
None # not a true value
and so returns False.
The expression:
any([(None, None), (None, None)])
likewise tests each element in turn to see if it is a true value. So it tests the first element:
(None, None) # a true value
which is a true value, because it is a non-empty tuple. So it returns True.
The rules for what are considered true and false values are:
False values:
False (of course!)
zero numbers (0, 0.0, Fraction(0), Decimal(0) etc)
empty strings and byte-strings “” and b""
None
empty containers like (), , {}, set(), frozenset()
any object where len(obj) returns 0
any object where the special dunder method __bool__ returns False.
True values:
True (of course!)
all objects by default including:
non-zero numbers
non-empty strings and byte-strings
non-empty containers
any object where len(obj) returns a non-zero length
any object where the __bool__ dunder method returns True.
So it doesn’t matter that your tuple contains false objects like None, the fact that it contains something rather than nothing at all means that it counts as a true value.