> I wish python had some equivalent to the switch statement that didn't involve workarounds with dictionaries, because sometimes you just want a clean way to deal with a value that can take 7 different forms.
I am with you there. I have often wished for a C-like switch(). That said, I think the best Python leans towards the functional, so instead of a switch(), it probably would be better to come up with some kind of multi-arm match more in line with modern functional language semantics. That could also be more flexible with respect to the type of the tested expression. Maybe something akin to the Rust match syntax.
> I regularly wish python required some sort type indication for function parameters, because dealing with libraries that take complex objects as function parameters can be an absolute nightmare.
Well, of course Python has type annotations. TBH, I don't use them. I tend to be rather pedantic about Sphinx doc for functions, most especially __init__() constructors. Kind of old-school and manual, but it works -- make doc and read it in your browser. The other thing I do in constructors is 1) make them very tolerant of what gets passed as a parameter, and 2) be very pedantic about raising ValueError in the right place, at the right time, with a clear message.
I have a good friend that I argue with regularly -- now this guy is a compiler front-end guru -- ex-Sun senior staff level C/C++ compiler tech lead, very smart -- but we argue regularly about Python idiom. He litters his Python with extremely un-idiomatic assert(isinstance()) to check incoming parameters. I think C poisoned this man's brain with respect to type checking.
There are two things wrong with his code: 1) It raises the wrong exception, the correct exception is TypeError, not AssertError. 2) It totally breaks __int__, __float__, etc, so I can't implement those on custom classes.
In my constructors, to the extent possible, I do "type checking by re-construction" -- parameters i and p gets run through something like:
self.i = int(i)
self.p = PClass(p)
So if you send me a string for i or something weird that implements __int__(), everything is cool. And PClass.__init__(self, x) is responsible for turning p into an instance of PClass, leaving it alone if it already is a PClass, or raises ValueError. This idiom makes type checking as tight as you want it to be, but doesn't break Python Zen.