> I can be much more confident in refactoring code because I know certain kinds of errors will be caught by during type checking.
I'll add to that: static typing also guarantees that your annotations are correct and complete. Prior to Mypy, in the Python world, we would document types in docstrings, which meant that docstrings gradually came to be incorrect--a function that returns a string might sometimes return `None`, but you called it assuming it always returned a string. In other cases, people (including the Python maintainers via the stdlib) will add annotations like "argument 'foo' is a file-like object", which is ... insufficient (do I just need a "read()" method? or do I need to implement "seek()" and "close()" as well?). Ultimately, in the absence of static analysis, a lot of time is wasted reading source code trying to understand the actual type signatures.
> Things have improved a lot in the Python ecosystem of late with the introduction of type annotations and mypy, so to the extent possible I treat Python as if it's a statically typed language, putting type annotations everywhere and always making sure the codebase passes type checking before pushing a commit.
This hasn't been true in my experience. Last I checked, Mypy still couldn't express things like recursive types (think "JSON" or "recursive linked list"), and a lot of things still required jumping through obscure hoops (defining a callback with kwargs). I also couldn't figure out how to export type annotations in my packages, and I had a lot of trouble getting mypy to pull in type stubs for packages that didn't provide their own annotations. In general, Python's typing story still feels very shoe-horned, although it's been a minute since I last fumbled with it, so maybe some of this has improved.