Now sit back and remember what C code looks like at runtime. It's just compiled to machine code. When you divide by zero in it, it happens on machine code level - i.e. the CPU actually attempts it, generates a hardware interrupt which the OS handles and ultimately terminates your process.
Python code, on the other hand, is just random binary data at runtime that another code - the interpreter - looks at and determines what to do. When you divide by zero in it, the interpreter double-checks the divisor and prints a nice traceback before the CPU actually attempts the operation.
The same goes for array bounds, object references etc.
In brief, at C level, there's no Python interpreter to babysit you. You need to check the input for validity yourself (for input originating from Python's runtime, however, there are library functions that ease the task). Likewise, if you wish to report an error to the Python runtime, you need to construct yourself and pass it an exception object with relevant info (exception type and parameters) for which, there's also a bunch of library functions.