Containment tests (the in operator) for strings are defined to check for the presence of a substring. See the footnote (1) in the docs for sequences.
No, it won’t change in the future.
Under the hood, what happens is that the in operator calls its right-hand operand’s __contains__ method, which for strings and bytes implement substring matching rather than element matching.
Your code snippet:
'a' and 'b' in {'a','b','c','d'}
is not testing whether ‘a’ is in the set. Try this:
'Not in the set' and 'b' in {'a', 'b', 'c', 'd'}
which will also return True. Python often reads very much like English, but this example is a false friend.
Rather than testing if ‘a’ is in the set and ‘b’ is also in the set, like English grammar would suggest, it is testing whether ‘a’ is a truthy value and ‘b’ is in the set. Since both expressions are true, the second is returned – which happens to be the True constant.
Try reversing the test and see what you get:
'b' in {'a','b','c','d'} and 'a'
The and and or operators are short-circuit operators. They work something like this:
def and_(a, b):
if not a:
return a
else:
return b
def or_(a, b):
if a:
return a
else:
return b
except that the second operand (b) isn’t evaluated unless it is needed.