One way of running programs in python is by executing a script, with run <script.py> in python or python <script.py> in terminal.
What if you realize that something in the script is wrong after you have executed the file, or for whatever reason you want to interupt the program?
You can use ctrl+c to abort the program which, essentially, is throwing an "exception" -- KeyboardInterrupt exception. We will briefly talk about Exception later in this notebook.
If you are writing some new code (to a python script) and you are unsure whether or not it will work, instead of doing run <script.py> and then manually interupting your code with ctrl+c, there are other more elegant ways. In this notebook, we will go over some ways of debugging in python.
In [1]:
# Let's first define a broken function
def blah(a, b):
c = 10
return a/b - c
In [2]:
# call the function
# define some varables to pass to the function
aa = 5
bb = 10
print blah(aa, bb) # call the function
As we know, 5/10 - 10 = -9.5 and not -10, so something must be wrong inside the function. In this simple example, it may be super obvious that we are dividing an integer with an integer, and will get back an integer. (Division between integers is defined as returning the integer part of the result, throwing away the remainder. The same division operator does real division when operating with floats, very confusing, right?).
But this is good enough to show why simple print statements will suffice in some cases.
Ok, assuming that we didn't know what the problem was, we will use print to find out what went wrong.
In [4]:
def blah(a, b):
c = 10
print "a: ", a
print "b: ", b
print "c: ", c
print "a/b = %d/%d = %f" %(a,b,a/b)
print "output:", a/b - c
return a/b - c
In [5]:
blah(aa, bb)
Out[5]:
From this, it's clear that a/b is the problem since a/b = 1/2 and not 0. And you can quickly go an fix that step. For example by using float(b), or by multiplying by 1. , the dot makes it a float.
But using print statements may be inconvenient if the code takes a long time to run, and also you may want to chcek the values of other variables to diagnose the problem. If you use crtl+C at this point, you will lose the values stored inside all variables. Perhaps you would want to go back and put another print statement inside the code and run it again to check another variable. But this goes on... and you may have to go back many times!
Alternatively, you can use the pdb module, which is an interactive source debugger. The variables are presevered at breakpoint, and can interactively step through each line of your code.
(see more at https://pymotw.com/2/pdb/)
To use, you can enable pdb either before an Exception is caught, or after. In Jupyter notebook, pdb can be enabled with the magic command %pdb, %pdb on, %pdb 1, disabled with %pdb, %pdb off or %pdb 0.
In [6]:
%pdb 0
In [7]:
% pdb off
In [8]:
% pdb on
In [9]:
%pdb
In [10]:
%pdb
After you've enabled pdb, type help to show available commands. Some commands are e.g. step, quit, restart.
If you have set pdb on before an exception is triggered, (I)python can call the interactive pdb debugger after the traceback printout.
If you want to activate the debugger AFTER an exception is caught/fired, without having to rerun your code, you can use the %debug magic (or debug in ipython).
If you are running some python scripts, where instead of running code line by line you want to run a large chunk of code before checking the variables or stepping through the code line-by-line, it's useful to use import pdb; pdb.set_trace().
If you know where you want to exit the code a priori, you can use sys.exit().
In [11]:
import sys
a = [1,2,3]
print a
sys.exit()
b = 'hahaha'
print b
In [ ]:
# Way to handle errors inside scripts
try:
# what we want the code to do
except: # when the above lines generate errors, will immediately jump to exception handler here, not finishing all the lines in try
# Do something else
In [ ]:
# Some Example usage of try…except:
# use default behavior if encounter IOError
try:
import astropy
except ImportError:
print("Astropy not installed...")
In [12]:
# Slightly more complex:
# Try, raise, except, else, finally
try:
print ('blah')
raise ValueError() # throws an error
except ValueError, Err: # only catches 0 division errors
print ("We caught an error! ")
else:
print ("here, if it didn't go through except...no errors are caught")
finally:
print ("literally, finally... Useful for cleaning files, or closing files.")
In [13]:
# If we didn't have an error...
#
try:
print ('blah')
# raise ValueError() # throws an error
except ValueError, Err: # only catches 0 division errors
print ("We caught an error! ")
else:
print ("here, if it didn't go through except... no errors are caught")
finally:
print ("literally, finally... Useful for cleaning files, or closing files.")
But sometimes you may want to use if... else instead of try...except.
In [14]:
import numpy as np
mask = [True, True, False]
print np.sum(mask) # same as counting number where mask == True
In [15]:
debug = False
if debug:
print "..."
In [16]:
debug = True
if debug:
print "..."
In [17]:
# define a number
x = 33
# print it if it is greater than 30 but smaller than 50
if x > 30 and x < 50:
print x
In [18]:
# print if number not np.nan
if not np.isnan(x):
print x
In [19]:
# Introducing numpy.where()
import numpy as np
np.where?
In [20]:
# Example 1
a = [0.1, 1, 3, 10, 100]
a = np.array(a) # so we can use np.where
# one way..
conditionIdx = ((a<=10) & (a>=1))
print conditionIdx # boolean
new = a[conditionIdx]
# or directly
new_a = a[((a <= 10) & (a>=1))]
# you can also use np.where
new_a = a[np.where((a <= 10) & (a>=1))]
In [ ]:
# Example 2 -- replacement using np.where
beam_ga = np.where(tz > 0, img[tx, ty, tz], 0)
# np.where(if condition is TRUE, then TRUE operation, else)
# Here, to mask out beam value for z<0
In [ ]: