It's like a security blanket for me.
It's like a security blanket for me.
Not to be a prick, but if you're having "typing" problems with Python, you're using it wrong.
Types are almost irrelevant in Python. What matters is whether, in a given situation, the method you're calling is supported and does what is intended. The actual type/class of the receiving object doesn't really matter, ditto for any arguments you pass to the method.
While this may seem highly unsafe and brittle, it has important benefits. You can just hack up some code that "just works" in a few minutes, without having to sacrifice a goat to the compiler first, making sure that all the correct types are specified, subclasses are defined, interfaces are implemented completely, etc. The rigid type systems in other languages often require this and more; making changes to your software becomes a chore. Rapid prototyping this is not.
In Python, when used correctly, you don't have this problem. Write a bit of code, write a test, run it, and decide what to do next. Ideal for testing out ideas. It doesn't work? Throw that shit out and write something else; two minutes later you'll be testing your new code again. Yes, you do need to have unit tests, but the same is (or should really be) true for most languages. Just because something compiles, doesn't mean that it does what you want. (Except maybe in Haskell. :-)
Interestingly, the quickly-written prototype might well be good enough to become the actual production code, with some additions. Just keep you tests up-to-date, for reasonable use cases, and there's no reason why your code shouldn't work in real life. Sure, that function foo(x) that wants to call x.bar() will fail if you call it with an x that doesn't support the bar() method. So don't do that, then! ;-)
Anyway, to summarize, I personally would much rather have Python's flexibility, paired with unit testing, than Java's lack of flexibility paired with compiler checks.
I'd say that strong compiler checks become more helpful as the size of a program increases. You can keep less of a big program in your head, and big programs have more code paths that could potentially need testing.
Either way, both flavors of static typing require development methods that are very different from Python's, though. My original point was that in Python, you shouldn't really have to worry about types, or checking them (in tests or otherwise). You can, but (IMHO) you'd be going against the grain of the language.
I understand the concept, but sometimes in a medium-size project, you just accidentally send the wrong object back. It happens. But Python won't tell you, and it'll blithely wait for the code to get exercised before dying. I don't remember exactly why I was having this problem, but the bug wasn't shallow, and required a certain confluence of exceptions to occur before it would fire. The sort of thing that would even escape most unit testing.
I would never force static typing on Python or Ruby or upon any language that didn't want it, and when I switched to Python I was very pleased with dynamic typing. However, I find myself much happier with the stronger compiler checks. Writing a medium project in Python now frays my nerves!
I agree that this does indeed happen... I think it's a matter of, the more flexible a language is, the more rope you get to hang yourself with, even unintentionally and unexpectedly.
(Anecdote:) For my work, I have been developing and maintaining a fairly large Python code base... (~70K lines... comparable to about 700K lines of Java <wink>). Another developer changed a module so it would access the database upon importing, which was a no-no (IMHO), so I changed it back, using a class that gets the desired info from the db upon instantiation, and some sort of singleton construct so the same instance would be reused. I also decided to write a test to check that when this module was first imported, there would be no instance of said class yet. To make sure it was a first import, I removed any references to the module from sys.modules, then did an import. This worked great... until I ran the whole test suite using nose [http://code.google.com/p/python-nose/], which has its own ideas about import order and such. This turned out to be surprisingly hard to fix... a non-shallow bug that wouldn't even be possible in less flexible languages.
Tests are effectively the answer, but they are a pain to write and having to document your type information via tests seems a lot more cumbersome from just doing "int my_var".
The party line with Python and dynamic typing is true in many cases, especially when starting a new project. As time goes on though, things get confusing and tests are rarely good enough to offer the same contract static typing offers. To say "you're doing it wrong" is somewhat correct, but no one does it right all the time.
import pdb;pdb.set_trace()
then run the program, and you'll get an interactive prompt and you can just look at everything in that state.
dir( myBuggyVariable)
Here is a link to doctests. There are godly, and unlike any testing library, in that they aren't a library, they're a comment scanner: http://docs.python.org/library/doctest.html
Smallest amount of rigmarole required to make testing happen I've seen in any language.
Java is almost untyped by comparison. Languages like Ocaml and F# occupy a middle ground.