On the other hand duck typing is largely a joke. Letting functions take anything as an argument and then just assuming it's a duck with the methods you want is no way to write a robust system. Performance wise you're boxed into a corner. The slow runtime will have you writing native code in another language, complicating your project. The GIL will have you jumping through hoops to work around.
As an offline data exploration and research tool limited to an individual or a small team, or for writing small utilities, I totally get it. For anything mission critical I'd much rather be in something like Java or C# where the typing situation is stronger, the performance is going to be much better, I have better access to threads if I need them, the reasons for dropping into native code are fewer, and the cross platform support works more seamlessly without additional layers like Docker.
Everyone keeps harping on type safety, but it just doesn't play out in reality. Linux Kernel is incredibly robust, has never been a broken mess, and has basically no type enforcement, as you can cast pointers into other stuff.
In general, all typing does is move error checking into the compiler/preprocessor instead of testing. And time spent on designing and writing type safe code is almost equivalent to time spent writing tests that serve as an end-to-end contract.
There is a reason why NodeJS was the most used language before all the AI stuff came with Python.
>Performance wise you're boxed into a corner. The slow runtime will have you writing native code in another language, complicating your project.
Most of these performance arguments are similar to arguging that your commuter car needs to be a track spec Ferrari in terms of performance, by people that have very little experience with cars.
Plenty of fast/performant stuff runs on Python. PyPy is a thing also. So is launching small compiled executables like a process, that takes literally one line of code in Python.
>The GIL will have you jumping through hoops to work around.
This is just laughable. Clearly you have extremely little experience with Python.
Do you write tests for every third-party function that interacts with your code, so that it never fails in runtime after a version bump?
How do you guarantee that your own refactoring is exhaustively covered by the prior tests you've written for the old version?
The exact granularity is a debate that has gone on for a long time. Nowadays, people seem to prefer larger tests that cover more code in one go so as to avoid lots of mocking / stubbing. Super granular tests tend to be reserved for libraries with no internal state.