The secret to successful programming is to run early, run often, and don't be afraid of things going wrong when you run your code the first time. Debugging, or finding mistakes in code, is a part of life for programmers. Here are some things that can happen:
- Your code doesn't run at all, usually because of a syntax error (you typed some illegal Python code).
- Your code runs, but the script doesn't complete and reports an error.
- Your code runs, but the script never completes. Often this occurs when you've created an infinite loop.
- Your code runs and the script completes, but it doesn't give you the expected result. This is called a logical error and it is often the type of error that takes the most effort to debug.
Don't be afraid of errors
Errors happen. There are very few programmers who can sit down and, off the top of their heads, write dozens of lines of bug free code. This means a couple of things for you:
- Expect to spend some time dealing with errors during the script-writing process. Beginning programmers sometimes underestimate how much time this takes. To get an initial estimate, you can take the amount of time it takes to draft your lines of code, then double it or triple it to accommodate for error handling and final polishing of your script and tool.
- Don't be afraid to run your script and hit the errors. A good strategy is to write a small piece of functionality, run it to make sure its working, then add on the next piece. For the sake of discussion let's suppose that as a new programmer you introduce a new bug once every 10 lines of code. It's much easier to find a single bug in 10 new lines of code than it is to find 5 bugs in 50 new lines of code. If you're building your script piece by piece and debugging often, you'll be able to make a quicker and more accurate assessment of where you introduced new errors.
Catching syntax errors
Syntax errors occur when you typed something incorrectly and your code refuses to run. Common syntax errors include forgetting a colon when setting a loop or an if condition, using single backslashes in a file name, providing the wrong number of arguments to a function, or trying to mix variable types incorrectly, such as dividing a number by a string.
When you try to run code with a syntax error in PythonWin, you may not notice anything happen. At the bottom of the window, look for a message such as "Failed to run script - syntax error - invalid syntax."
Sometimes the message is clearer. For example, if you indent a line only three spaces instead of four, you get: "Failed to run script - syntax error - unexpected indent."
You can check for syntax errors before you run your code using the Check button on the PythonWin Standard toolbar. This button checks for errors and reports them in a small message at the bottom of the window, just as you would see if you tried to run your code with a syntax error. If there are no errors, you'll see a message such as "Python and the TabNanny successfully checked the file 'myScript.py.'" (The TabNanny is a module that PythonWin uses to check for correct indentation.)
Dealing with crashes
If your code crashes, you may see an error message in the Interactive Window or the console. Instead of allowing your eyes to glaze over or banging your head against the desk, you should rejoice at the fact that the software possibly reported to you exactly what went wrong! Scour the message for clues as to what line of code caused the error and what the problem was. Do this even if the message looks intimidating. For example, see if you can understand what caused this error message:
Traceback (most recent call last): File "C:\Python25\Lib\site-packages\pythonwin\pywin\framework\scriptutils.py", line 310, in RunScript exec codeObject in __main__.__dict__ File "C:\PSU_Python_Practice\syntax_error_practice.py", line 4, in <module> x = x / 0 ZeroDivisionError: integer division or modulo by zero
Although the message begins with some content you probably don't understand, you can reasonably guess that the error was caused in Line 4: x = x / 0. Dividing by 0 is not possible and the computer won't try to do it.
It's easier to interpret messages like this if you've displayed line numbers for your code in PythonWin. To get the line numbers:
- In PythonWin click View > Options.
- Click the Editor tab.
- Set the Line Numbers property to a higher number such as 30.
The line numbers are also helpful if you make an e-mail or forum posting about your code and include the script. You can immediately point out the line of the crash to your colleagues. If you e-mail code to the instructors during this course asking for help, be prepared to get a response pointing out specific line numbers that need attention.
Sometimes it's easy to sprinkle a few 'print' statements throughout your code to figure out how far it got before it crashed, or what's happening to certain values in your script as it runs. This can also be helpful to verify that your loops are doing what you expect and that you are avoiding off-by-one errors.
Suppose you are trying to find the mean (average) value of the items in a list with the code below.
#Find average of items in a list list = [22,343,73,464,90] for item in list: total = 0 total += item average = total / len(list) print ("Average is " + str(average))
The script reports "Average is 18," which doesn't look right. From a quick visual check of this list you could guess that the average would be over 100. The script isn't erroneously getting the number 18 from the list; it's not one of the values. So where is it coming from? You can place a few strategic print statements in the script to get a better report of what's going on:
#Find average of items in a list list = [22,343,73,464,90] for item in list: print ("Processing loop...") total = 0 total += item print (total) print (len(list)) average = total / len(list) print ("Performing division...") print ("Average is " + str(average))
Now when you run the script you see.
Processing loop... 22 Processing loop... 343 Processing loop... 73 Processing loop... 464 Processing loop... 90 5 Performing division... Average is 18
The error now becomes more clear. The running total isn't being kept successfully; instead, it's resetting each time the loop runs. This causes the last value, 90, to be divided by 5, yielding an answer of 18. You need to initialize the variable for the total outside the loop to prevent this from happening. After fixing the code and removing the print statements, you get:
#Find average of items in a list list = [22,343,73,464,90] total = 0 for item in list: total += item average = total / len(list) print ("Average is " + str(average))
The resulting "Average is 198" looks a lot better. You've fixed a logical error in your code: an error that doesn't make your script crash, but produces the wrong result.
Although debugging with print statements is quick and easy, you need to be careful with it. Once you've fixed your code, you need to remember to remove the statements in order to make your code faster and less cluttered. Also, adding print statements becomes impractical for long or complex scripts. You can pinpoint problems more quickly and keep track of many variables at a time using the PythonWin debugger, which is covered in the next section of this lesson.