The pythonic mantra is derived from the Robustness principle – you’re supposed to be clear about what you’re sending out but as tolerant as you can of what comes in. In most cases that boils down to ‘duck typing’ - using the presence of properties or methods you need: worrying about whether the object does what you need it to do and not it’s ‘type’ is way to stay sane.
From an implementation perspective there’s 3 levels of response:
When you really care, you can add ‘type safety’ – or more precisely, some kind of invariant checking – via decorators. Effectively you just add a decorator that describes the argument list you expect and raises an exception when the incoming arguments are the wrong kind. This is extra work – since you have to create and maintain the system – but its a good way to stop things when bad stuff has happened, capture a stack trace, and point yourself at the culprit who is somewhere farther down in the stack.
For most applications, it’s just easier to add the invariant checks you need inside your functions. You know the most about your expected conditions when you’re writing a function, so it’s nice to be able to just add a few lines right that raise meaningful errors if your expectations aren’t met. If you can be sure that conditions are right in the first few lines you can proceed without worrying – you end up saving a lot of much more complex in-line error checking in the guts of your actual work.
And of course for a lot of things you can just let it fail. If you’re expecting a string and you get something else, you will get one of a handful of errors - a few months of python and you’ll know them all. Raising your own exception is good when you want to tell your future self something that s/he might not know. It’s probably not worth the effort to write a few lines to say “argument X is None, should be string” when you’re going to get “NoneType has no attribute ‘upper’” anyway.
In a language where passing None instead a string is going to raise and exception (instead of a C++ style hard crash or BSOD when you somehow pass the wrong kind of memory bucket) type errors are just improperly policed logic errors. I kept stats on my error logs for two years, and only 6 percent of my errors were type errors – and almost all of them were actually caused by None Type has no XXX
problems, which would have required null checks in the runtime code anyway. Implementing type safety would have saved maybe 1-2% of my bugs – and let’s not get into a flame war on all the bugs created by casting and all the workarounds you need to do what you need to do when ‘string’ and ‘unicode’ and ‘char[]’ are all different things