Part 1 – Part 2 – Part 3 – Part 4– Part 5 – Part 6 – Part 7
I plan to get my Microsoft Technology Associate certificate in Python programming and figured I’d take my notes publicly as I prepare. Call it learning in public or review notes or whatever – I’m just interested in passing on my first attempt.
My aim is to review the topics I already know and cover topics I might have missed. I will link resources as opportunities arise. There will be lots of code examples provided, if for no other reason than to prove to myself that things work the way I expect.
I’m certainly interested in corrections if I have something blatantly wrong. However, the point of these posts isn’t to go super deep on each topic or act as a tutorial, but rather to act as a broad overview review of the topics (with perhaps some deeper dives where necessary) for this exam. I think it’s safe to say that if you don’t understand the code examples I provide here, you should review the corresponding topic in the Python documentation and other deep-dive tutorials.
This is both a shorter list than other sections and comprises only 5 to 10 percent of the exam, but these topics are what make or break you as a software developer. If you’re not able to debug code or plan around predictable errors, you’re going to have a rough time keeping up with your users.
Analyze, detect, and fix code segments that have errors
This is a skill that comes from experience with writing and debugging code. It will probably help to tackle some debugging exercises as a little extra practice, but ultimately, just writing a lot of code will lead to the best, most realistic practice in debugging.
In writing basically any project, you will inevitably run into issues and will have to solve those issues to move forward. Writing intentionally bad code here for debugging practice just doesn’t seem to be the best practice to expand this skillset, but we can at least define some terms.
Syntax, logic, and runtime errors
I’ll describe how I understand these different types of errors, then I’ll look up some documentation and fill in accordingly.
Syntax errors occur when the rules of a programming language require a particular way of writing code and those rules are violated. We often see these with punctuation in the form of missing commas, brackets, parentheses, and the like. When a syntax error is present in source code, the file will not compile and run until the syntax error is corrected, but (generally) the error message will give us at least a hint of where we can find the offending code.
Logic errors occur when the code doesn’t work quite the way its developers intend. This often includes mathematical errors, like an incrementer that increases by two instead of one or an algorithm that isn’t quite implemented correctly. Logic errors can be the most insidious and hard-to-find errors in code, as often they don’t break the program but instead silently return faulty data.
Runtime errors occur while a program is running in response to unexpected (or unhandled) conditions with data. A common runtime error is when a program executes a mathematical function on a variable that contains a string type or a string method on a numerical value.
In reading around, I feel like I did a pretty good job defining these. For a more thorough explanation that gets into a number of different cases and includes code examples to run, check out this article from the University of Waterloo’s Computer Science program.
Analyze and construct code segments that handle exceptions
We can plan around predictable or particularly devastating errors by combining a number of keywords available in Python. Here’s the official documentation on the subject. Let’s build up an example to show how each of these keywords works in conjunction with each other.
try
Let’s say we’re looking for a numerical value from the user. We can get an input()
value and convert that to an integer with int()
pretty easily:
order = int(input("Enter the order to execute: "))
The problem we face here is that if the user enters non-numerical characters, Python is going to throw a runtime error known as a ValueError
:
Enter the order to execute: one Traceback (most recent call last): File "C:\local\path\to\file.py", line 1, in order = int(input("Enter the order to execute: ")) ValueError: invalid literal for int() with base 10: 'one'
By putting an error-prone statement like this inside a try
block, Python can first try that code and then handle any errors if they occur. The syntax of a try statement is really simple:
try: order = int(input("Enter the order to execute: "))
A try
statement alone isn’t going to cut it, though. When I try to run this file, Python throws a syntax error:
except
A try
statement requires an except
statement to be syntactically valid. It might look something like this:
try: order = int(input("Enter the order to execute: ")) except: print("That's not a valid order number!")
output
Enter the order to execute: one That's not a valid order number!
While practically this example isn’t doing much (printing an error statement isn’t that much different than throwing an error, after all), what if we wrapped all of this inside a while
loop?
while True: try: order = int(input("Enter the order to execute: ")) break except: print("That's not a valid order number!")
output
Enter the order to execute: one That's not a valid order number! Enter the order to execute: two That's not a valid order number! Enter the order to execute: 3
We can add any number of statements to a try
clause, and an error on any of them will invoke checking the except clause(s), skipping everything that’s left in the try clause (in the above example, the break
statement that takes us out of the while
loop).
What I’ve written here is not particularly good practice, however. An except
clause should ideally specify one or more errors to check for.
while True: try: order = int(input("Enter the order to execute: ")) break except ValueError: print("That's not a valid order number!")
This approach means we’re not handling unexpected errors in a way that is not planned. If in the example above, say, a TypeError were encountered, Python would still raise that error since it is not handled in an except clause.
We could alternatively handle that theoretical TypeError in one of two other ways. First, we can handle multiple errors with the same code by using a tuple of comma-separated error names in our except
clause:
while True: try: order = int(input("Enter the order to execute: ")) break except (ValueError, TypeError): print("That's not a valid order number!")
Second, we can have any number of except
clauses with each handling a different error in different ways:
while True: try: order = int(input("Enter the order to execute: ")) break except ValueError: print("That's not a valid order number!") except TypeError: print("I don't even know how you triggered this one!")
else
We’ve met the else
statement before, but it is has a special use in a try/except
statement. When used, it must be after all except
clauses. It contains code that is executed if and only if no except
statement conditions are met.
In a practical sense, this is kind of the same as adding more blocks of code within the try
statement, but this is also considered bad practice. By introducing more code statements inside the try
clause, we’re introducing more opportunities for unhandled errors. Even though there’s not really a scenario where our break
statement would throw an error, it would still be best practice to refactor our above code like so:
while True: try: order = int(input("Enter the order to execute: ")) except ValueError: print("That's not a valid order number!") else: break
output
Enter the order to execute: one That's not a valid order number! Enter the order to execute: 1
This works the same as before, but is protecting only the code of concern inside the try
statement this way.
finally
Another associated keyword, finally
, is used to execute code whether or not an exception was encountered during execution of a try/except
statement.
For example, let’s say that we don’t really want to ask the user the order to execute more than once. Even it throws an error, we want to break out of the loop in question after the first attempt to ask the user. finally
achieves this for us:
while True: try: order = int(input("Enter the order to execute: ")) except ValueError: print("That's not a valid order number!") finally: break
output 1
Enter the order to execute: 1
output 2
Enter the order to execute: one That's not a valid order number!
In both of these cases, the loop breaks after the question is asked just one time, whether or not the user’s response is valid. We can combine all four of these keywords to break after the first time the question is asked, but if a number successfully entered, printing that number:
while True: try: order = int(input("Enter the order to execute: ")) except ValueError: print("That's not a valid order number!") else: print(f"Execute order {order}.") finally: break
output
Enter the order to execute: 66 Execute order 66
Good, good.
raise
We can manually raise errors by using the raise
keyword. Let’s say we’re a particularly goal-oriented leader that really only wants to execute order 66. We can add further checks inside the else
clause here to ensure that’s the case.
while True: try: order = int(input("Enter the order to execute: ")) except ValueError: print("That's not a valid order number!") else: if order == 66: print(f"Execute order {order}.") else: print("Now, young Skywalker, you will die.") break
output
Enter the order to execute: sixty six That's not a valid order number! Enter the order to execute: 69 Now, young Skywalker, you will die.
Here, if the user enters a string that is successfully converted to an integer, we are taken to the else
clause. Here, we check to see if the integer entered equals 66. If it does, the order is executed. What if we instead wanted to raise an error if the correct order number is not entered?
By using the raise
keyword, a programmer can force a specified error to occur. If we want to check for a particular error in a try
statement, catch for it with an except
, and then re-raise the exception later, we can just use raise
by itself, like so:
while True: try: order = int(input("Enter the order to execute: ")) except ValueError: print("That's not a valid order number!") raise else: if order == 66: print(f"Execute order {order}.") else: raise TypeError break
output
Enter the order to execute: sixty six That's not a valid order number! Traceback (most recent call last): File "C:\local\path\to\file.py", line 3, in order = int(input("Enter the order to execute: ")) ValueError: invalid literal for int() with base 10: 'sixty six'
Here we can see that the except
clause is still catching this error with the print()
statement, but instead of the while
loop catching it and taking us back to the try
clause, the lone raise
refers back to the ValueError
encountered earlier and raises it.
Further, if the order number entered is not correct, we raise a TypeError:
Output
Enter the order to execute: 69 Traceback (most recent call last): File "C:\Users\dbrown\OneDrive - FSU Foundation\documents\Python\notes\python_examples.py", line 11, in raise TypeError TypeError
One can always go deeper with errors and error handling, but this is a good start that should help us all crush Microsoft’s exam.