I've definitely been bitten by this before, accidentally passing in a book parameter instead of an int, and having some math work initially and then blow up months later.
Knuth argues against this kind of implicit bool to integer conversion in Concrete Mathematics.
There's a lot of history of languages without a real boolean type using integer 0 and 1 as the sentinel values indicating false-ness and true-ness.
And Python, in a nod to that, implements bool as a subclass of int, of which only two instances can ever exist (with False having a value of 0 and True having a value of 1).
So this isn't weak typing -- isinstance(True, int) is True in Python, and operations like the ones you're mentioning work because of that, for the same reason any other subclass of int would work.
But I think there's a fundamental divide about what weak typing actually is. For example, it's entirely possible in Rust to have implemented Add for i32 and String. Even this isn't weak typing in its traditional form.
AFAIK, in this sense weak typing refers to whenever the compiler will implicitly cast one type to another in order to satisfy a type constraint. C's number types do this. PHP does this on everything. Javascript has, eg., [] + [] == "". Python does not do this - all type casts are explicit in the implementation of the operator or function (which is implemented on the type, not globally).
It's reasonable to argue that the use of the phrase "weak typing" to mean "things I don't like about a type-system" is not only well established but maybe even more useful. It does, however, irritate Python programmers who don't want their language lumped in with PHP.
>>> isinstance(type, object)
true
>>> T = type('T', (), {})
>>> isinstance(T, type)
True
>>> isinstance(T, object)
True
>>> isinstance(T(), object)
True
>>> isinstance(int, object)
True
>>> issubclass(int, object)
True
>>> isinstance(1, object)
TrueFirst off, see my other comment about Python's boolean type and the fact that it is an integral type:
https://news.ycombinator.com/item?id=9595988
Second, boolean evaluation of arbitrary types in Python is not "weak" -- it's well-defined through a standard language-level protocol, and it's always very clear what can and can't be evaluated as a boolean and what boolean values will result from doing so.