and and or short-circuit, meaning if the left side makes the expression always True or always False, the right side won’t even be evaluated:

False and f() # Doesn’t call f, because if one is False the result is always False
True or f() # Doesn’t call f, because if one is True the result is always True
False & f() # Calls f anyway
True | f() # Calls f anyway

There’s no such short-circuiting left hand for exclusive-or (in a ^ b, no matter what a is, b could change the overall value), so there’s only a need for one operator.

To go into a bit more detail: &, |, and ^ are overloadable operators, meaning that types can define them to produce any value. (In the common case of ints, they act bitwise.) and and or aren’t, and always evaluate to one of their two operands: the left one if it’s falsy and the right one otherwise in the case of and, and the left one if it’s truthy and the right one otherwise in the case of or. For a hypothetical xor operator, it’s not clear which of these would be chosen.

>>> True and "hello"
"hello"
>>> 0 and True
0
>>> True or ""
True
>>> False or ""
""
>>> "truthy" xor "also truthy"
??? – can’t be either of these, because it has to be logically false