IPython is more than a Python shell with syntax coloring, it comes with a variety of useful tools and functions, for instance the %time and %prun commands for timing and profiling any statement:
In: %time (1 + 1) CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s Wall time: 0.00 s Out: 2
There is also the integration with the Python debugger (pdb), but I still need to learn how to use this :) Meanwhile, here is a useful trick I was shown recently: using the embed() function from the IPython module, you can invoke an IPython shell anywhere in your code. The context of the shell will be that of the line where it is invoked, meaning you are able to access local variables with their current values at execution time. This is particularly handy, for instance, to debug exceptions:
try: my_dangerous_function() except: import IPython IPython.embed() # spawns a shell within the current context
Once you are done with the shell, just exit() it (or Ctrl-D) and the execution will resume from the point it was interrupted at. Note that this feature is only available with recent versions of IPython (0.13.1 on my machine).