A few notes:
Python's newer abstract base classes allow you to make types that specify the presence of abstract methods and properties and you can use isinstance(thing, base_class) to achieve something similar (and thereby making handling types more familiar to foreigners)
With respect to Java and call stacks; Java has no easy way to dispatch to a function by name. You either need to make a class hierarchy so that you can use virtual call dispatch; or you need an if-tree (which is ugly but underappreciated). I've seen a few Java brains melt when I give them something like:
Of course, I could just make a base class for spells and grow a giant, sparse API; but the benefit of playing the default-implementation / function overriding game is suboptimal. At the end of the day, the class hierarchy is just a data structure to determine how different "types" of functions get dispatched. As it happens, it spreads things out in a way that makes them hard to visualize. It often forces me to shadow unrelated sections of other APIs that grow on the same base-class.
In short, it's a crappy data structure for the purpose and it makes a pain out of gathering the knowledge to know how that dispatch happens. Sure, the code is "type-correct", but that doesn't say very much about being logically correct. As it happens, type errors are easy errors--but figuring out how the class hierarchy isn't serving your logical needs is *not* an easy error.
Don't get me wrong, I'm not fundamentally opposed to a good type system. First-class functions in Haskell or the like could handle this with type-safety. However, our industry just isn't ready for all of the pipes, beards, and sweaters. So, given the choice between Python and Java, I pick the one that give me methods for dynamic dispatch that don't abuse an already incredibly limited inheritance mechanism. I'll take the language that allows extensible, generic function application instead of getting it as a side-effect of their implementation of an already stunted type-system.