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.
The topic today is the last of the three largest sections of the Microsoft Technology Associate certification exam for Python:
Thankfully, this is again a relatively straightforward list of commands and topics.
Construct and analyze code segments that perform file input and output operations
Let’s say we have a file named lines.txt
with the following lines:
First line Second line Third line Four!
We can use Python to open and interact with this file in various ways. Here’s the Python documentation on this topic.
Using open
and close
Assuming it is in the same directory as our Python script, we can open, read, and close the file with the following commands:
f = open("lines.txt", "r") text = f.read() # full text of file is now in variable text f.close()
If the file doesn’t exist, Python throws an error:
Traceback (most recent call last): File "C:\path\to\file.py", line 1, in file = open("lines.txt", "r") FileNotFoundError: [Errno 2] No such file or directory: 'lines.txt'
Using with
It’s important to close the file or it can have all kinds of unexpected consequences. For this reason, the following syntax is much more common for file input:
with open("lines.txt", "r") as f: f.read() # do any other file interactions in indented block
Using with open()
lets you do all file interactions within the indented block and automatically closes the file after leaving the block. This is much more convenient and secure than explicitly closing files.
The second argument, "r"
, indicates that the file is to be opened in read mode. This is technically an optional argument; if omitted, the file will default to opening in read mode. Remember, however, that explicit is better than implicit – it’s best to specify the mode.
Using read
, readline
, and readlines
File objects have three basic and useful methods to read from a file. The first has been shown already: read()
with open("lines.txt", "r") as f: print(f.read())
output
First line Second line Third line Four!
We can represent what that string looks like using the repr()
function:
with open("lines.txt", "r") as f: print(repr(f.read()))
output
'First line\nSecond line\nThird line\nFour!'
Alternatively, we can use the readline()
method, which reads through each line of the file per call. Example:
with open("lines.txt", "r") as f: print(f.readline()) print(f.readline())
output
First line Second line >>>
Note that Python automatically adds a \n
character to the end of each print()
call. We can override this by adding an optional argument to our print call: print(f.readline(), end='')
.
Finally, the readlines()
method returns the lines as a list, with one line per list member.
Creating and writing to files
When we opened our file with open("lines.txt", "r")
, the second argument "r"
signified that we were opening the document in read mode. If we instead send the argument "w"
, we are opening the file in write mode.
If a file does not exist and is opened in write mode using open("file_name.txt", "w")
, the file will be created automatically.
If we try to read from the file while in write mode, Python throws an error:
with open("lines.txt", "w") as f: text = f.read()
output
Traceback (most recent call last): File "C:\path\to\file.py", line 2, in text = f.read() io.UnsupportedOperation: not readable
When an existing file is opened in write mode, it is emptied of its contents. So even though an error was thrown in the last example, the file was still opened in write mode prior to the error, meaning if we correctly open in read mode and print, the file is now empty.
Note that the write()
method returns the number of characters written, should that ever be useful. I have literally never used that functionality, but it’s nice to know it’s there.
Let’s open the file in write mode and write a couple lines to it:
with open("lines.txt", "w") as f: f.write("First line all over again") f.write("Second line for the second time.") with open("lines.txt", "r") as f: print(f.read())
output
First line all over again.Second line for the second time.
See the problem? Unlike the print()
function, the write()
method does not automatically add a new line. New-line characters need to be added manually.
Another property of opening a file in write mode is that if the file doesn’t exist, Python will create the file for us. Deleting the file before running the same code as last time (with \n
characters added) does not throw an error:
with open("lines.txt", "w") as f: f.write("First line all over again.\n") f.write("Second line for the second time.\n") with open("lines.txt", "r") as f: print(f.read())
output
First line all over again. Second line for the second time. >>>
append
mode
Append mode allows us to write to the end of a file that aleady has existing data:
with open("lines.txt", "w") as f: f.write("First line all over again.\n") f.write("Second line for the second time.\n") with open("lines.txt", "r") as f: print(f.read()) with open("lines.txt", "a") as f: f.write("Third line should indeed be third.\n") with open("lines.txt", "r") as f: print(f.read())
output
First line all over again. Second line for the second time. First line all over again. Second line for the second time. Third line should indeed be third. >>>
Other modes
The Python docs indicate that opening a file in "r+"
mode allows both reading and writing, and while this is true, I’ve found it to result in behavior that is unexpected. This is certainly an area for further review.
We can also open files in binary mode, using them to read the binary code within. This is indicated with "rb+"
. I’d guess this is beyond the scope of Microsoft’s MTA certification exam, but it’s worth being aware of.
Checking for the existence of a file
I’ve traditionally used the os
module to check for the existence of a file, but I’m inclined to believe that this exam is still focusing on built-in methods. Still, I wasn’t sure the best way to do this, so I searched around and found this great article by Dan Bader on the subject. He’s confirmed for me that using the following is likely the best way to do this:
import os os.path.exists('lines.txt')
os.path.exists()
returns True if the file does exist and False if it does not.
Another valid option is simply to combine a try
statement with the open()
call to catch any errors based on files that don’t exist.
try: with open("lines2.txt", "r") as f: print(f.read()) except FileNotFoundError: print("File does not exist!")
output
File does not exist!
Bader goes on to mention a third method using the pathlib
module, but that is likely beyond the scope of Microsoft’s exam (based on the external modules that will be covered in part six of this prep series).
Deleting a file
This is again an area where I have used the os
module to do this and wondered if there is a better way. It doesn’t seem like there is. This is very simple:
import os os.remove("name_of_file.txt")
Easy!
Construct and analyze code segments that perform console input and output operations
Input is relatively simple, but output will be more involved.
Read input from the console
Reading string input from the console in Python is extremely easy:
synth = input("Please enter your first synthesizer: ") print(synth)
output
Please enter your first synthesizer: Volca Keys Volca Keys >>>
If you want to return a value of a particular type, just coerce the desired type from the input statement:
age = int(input("Please enter your age: ")) print(age) print(type(age))
output
Please enter your age: 900 900 <class 'int'> >>>
If you’re looking for a particular data type like in the above example, it’s best practice to wrap the input
statement inside a while
loop and a try
clause to ensure data validity.
age = None while type(age) is not int: try: age = int(input("Please enter your age: ")) except ValueError: print("You need to enter an integer!") print(age) print(type(age))
output
Please enter your age: yoda You need to enter an integer! Please enter your age: 900 900 <class 'int'> >>>
Print formatted text
This is an extremely deep topic for such a deceptively simple question. Python has several ways to format strings. I’ll start with the Python docs, as I won’t go deeper than those, but I’ll also cover each one here briefly.
Using slicing and concatenation
We can construct our output using operators, functions, and methods we’ve already covered:
increase = 5000 print("I would like my paycheck to be $" + str(increase) + " more.").
output
I would like my paycheck to be $5000 more.
Using string slicing and concatenation is absolutely still a valid approach should it be necessary, but personally, I find f-strings to be far superior.
The f-string
By using f-strings, we avoid having to coerce different data types, our code is more readable, and we don’t have to add multiple +
operators to construct strings that use variables.
We can begin our strings with f
or F
before the opening quotation mark to open a whole world of formatting possibilities:
increase = 5000 print(f"I would like my paycheck to be ${increase} more.")
output
I would like my paycheck to be $5000 more.
We can break f-strings over multiple lines, and because of Python’s implicit line continuation within parentheses, it is not necessary to use a +
operator. (Yes, yes, I know that I earlier called out explicit being better than implicit, but just as important is that simple is better than complex.)
name = "Inigo Montoya" dastardly_deed = "killed my father" consequence = "die" print( f"My name is {name}. " f"You {dastardly_deed}. " f"Prepare to {consequence}." )
output
My name is Inigo Montoya. You killed my father. Prepare to die.
With the curly braces, we can perform operations and use operators on variables:
name = "adoY" dastardly_deed = "ARE TOO OLD FOR THE TRAINING" consequence = " be trained anyway " age = 300 print( f"{name[::-1]}, my name is.\n" f"You {dastardly_deed.lower()}.\n" f"Prepare to {consequence.strip()}.\n" f"When {age*3} years old you reach,\n" "look as good you will not." )
output
Yoda, my name is. You are too old for the training. Prepare to be trained anyway. When 900 years old you reach, look as good you will not.
All of this is really cool.
One final thing I just learned from reviewing the docs. We can convert the value before it is formatted by adding one of the following to the end of the variable name within the curly braces: !a
applies ascii()
, !r
applies repr()
, and !s
applies str()
:
what_it_is = "treason" print(f"It's {what_it_is!r} then.")
output
It's 'treason' then.
Is it more or less threatening with the scare quotes?
The str.format()
method
Kind of similar but way less cool (in my opinion) is the format method. I’ve found this to be most useful when I want to have a pre-prepared string with variable values to be determined later, like this:
string_with_variables = "You're a {}, {:10}!" what = "wizard" who = "'arry" print(string_with_variables.format(what.upper(), who))
output
You're a WIZARD, 'arry !
You still need to use curly braces for placeholders, but you also add a .format(arguments)
with a number of arguments equal to the number of placeholders in the string. I needed to use the upper()
method on the variable itself, but the formatting of the variable is done in the curly braces (the example here specified to use 10 places for the string even though it wasn’t that many characters).
The old-school Python 2 %s
operator
This thing still works as Python 2 users remember:
target = "target" print("Stay on %s" % target)
output
Stay on target
While still in the language, I consider this virtually deprecated and clearly inferior to the f-string as well.
Use of command line arguments
The last item to cover today, and my initial thought was that this is just too simple. Aren’t command-line arguments just added with spaces in between after calling a script from the command line? The first example that comes to mind are various command-line arguments sent to the Django’s manage.py
file:
python manage.py makemigrations python manage.py migrate python manage.py runserver
I’m was not at all convinced that this covered the whole subject, so I consulted the internet and came up with this article on the topic by Frank Hoffman. It turns out that there is, in fact, much more to this subject than I had previously encountered.
Hoffman covers three methods: using the sys
module, using the getopt
module, and using the argparse
module, which are all standard modules. He also mentions the docopt
module which is available on GitHub. Big thanks to Mr. Hoffman for his very informative post.
Based on sys
being the only of these modules specified by Microsoft to know for this exam and the fact that this is already a very long post, I’m going to quickly break down the sys
method here.
It just takes these two lines of code to see a list of command-line arguments sent with a script call:
import sys print(sys.argv)
output
['C:\\path\\to\\file\\python_examples.py'] >>>
If you call the script by running it from IDLE (or your IDE of choice), the only command-line argument is the full path of the script.
Instead of calling from an IDE, we should instead use this functionality as intended, from the command line. Note that as the returned item is a list, we can also access individual arguments with subscripts.
import sys print(sys.argv[0])
console
λ python python_examples.py python_examples.py
What if we wanted to send other command-line arguments?
import sys print(sys.argv)
console
λ python python_examples.py -o --one "arguments with spaces get quotation marks" ['python_examples.py', '-o', '--one', 'arguments with spaces get quotation marks']
Now my wheels are spinning. If we can send whatever we want with a script call, we can handle those arguments within the script itself, opening limitless possibilities without having to go through endless dialogue menus.
This is a legitimate level-up for me to end this post and I’m looking forward to incorporating this skill into my toolset. Part four incoming soon.