Leaving the other points which are valid in some situations and not in others...
> It's easy for a compiler to check that a function you're calling requires 3 arguments of type foo, bar and baz
No, it's not. It's actually impossible to determine, since you can reassign any function at runtime. Essentially you can do:
self.some_func = one_implementation
...
self.some_func = another_implementation
Those two implementations don't have to have the same types, can accept any number of parameters, can be wrapped in something else, etc. etc. Compiler cannot figure it out ahead of time.
On the other hand, it opens a lot of new possibilities that are not available in compile-time type checks. For example mocking objects in other modules:
def test_some_other_module(self):
tested_module.SpecificClass = Mock()
tested_module.SomeFactory.getBlah(1,2,3)
self.assertTrue(tested_module.SpecificClass.called_with([1,2,3]))
reload tested_module
Whether it introduces other bugs is a different discussion... Sure, it's worse than Haskell where things tend to work the first time they compile, but on the other hand I get more annoyance from Java-style types than they're worth in practice. YMMV