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 interesting, because I have used each one of these modules at various times, but I haven’t gone deep on learning the ins and outs of any one of them. Given that this section makes up only one to five percent of the exam, I can’t imagine testers need to learn everything about these modules, but it can’t hurt to review some of the most common features of each.
math
Here’s the full documentation for this module. Reading down the methods, many of them I can’t imagine needing for this exam, but there are a handful that seem very likely to come up.
math.pow(x, y)
This returns the value of x
raised to the y
power. This method converts both arguments to floats. If you want to perform integer operations, use the **
operator.
>>> import math >>> math.pow(4, 4) 256.0 >>> 4**4 256
math.sqrt(x)
Inversely, this method returns the square root of x
, again as a float, even if the result is could be an integer.
>>> import math >>> math.sqrt(25) 5.0 >>> math.sqrt(24) 4.898979485566356
math.ceil(x)
and math.floor(x)
These methods round a number up and down, respectively, to the nearest integer in that direction.
>>> import math >>> math.ceil(4.98) 5 >>> math.floor(4.98) 4 >>> math.ceil(5) 5 >>> math.floor(5) 5
math.pi
and math.e
The math module also includes various mathematical constant values, including pi and e:
>>> import math >>> math.pi 3.141592653589793 >>> math.e 2.718281828459045
There are many more methods in this module that perform a number of mathematical operations that may be necessary in certain kinds of development. These six properties seem to me to be the most likely ones that will be tested on Microsoft’s Python certification exam.
datetime
This is a super useful module for web development that allows a programmer to work with dates and times in a much more easy and efficient way. It works by default using the standard Gregorian calendar. Here’s the documentation for the module.
Data types in datetime
Initializing the following methods gives us classes that hold the current date, the current time, or the current date and time:
>>> import datetime >>> datetime.date <class 'datetime.date'> >>> datetime.time <class 'datetime.time'> >>> datetime.datetime <class 'datetime.datetime'>
These each require values, so initializing in this way doesn’t really do anything except create an empty class. There are a couple useful functions we can use on date
and datetime
objects. Weirdly, there does not appear to be a corresponding one just for the current time.
>>> import datetime >>> datetime.date.today() datetime.date(2020, 1, 5) >>> datetime.datetime.now() datetime.datetime(2020, 1, 5, 8, 33, 2, 84329)
The values are broken down by units of time in this order: year, month, day of the month, hour, minute, second, and microsecond.
The today()
method also works on datetime
objects:
>>> datetime.datetime.today() datetime.datetime(2020, 1, 5, 8, 33, 56, 980197)
Note that neither work with time
objects, and now()
doesn’t work with date objects:
>>> datetime.date.now() Traceback (most recent call last): File "", line 1, in AttributeError: type object 'datetime.date' has no attribute 'now' >>> datetime.time.now() Traceback (most recent call last): File "", line 1, in AttributeError: type object 'datetime.time' has no attribute 'now' >>> datetime.time.today() Traceback (most recent call last): File "", line 1, in AttributeError: type object 'datetime.time' has no attribute 'today'
The module contains two other data types: datetime.timedelta
, which represents the duration between two dates, and datetime.tzinfo
, an abstract class that handles time zone information.
timedelta
objects
timedelta
objects can be used to represent, calculate, convert, or otherwise handle the duration between different datetime
objects. Here’s a simple example that shows the amount of time between two calls to datetime.datetime.now()
:
>>> from datetime import timedelta >>> call_1 = datetime.datetime.now() >>> call_2 = datetime.datetime.now() >>> call_2 - call_1 datetime.timedelta(seconds=2, microseconds=264246)
Negative values in time deltas are hard to grok:
>>> call_1 - call_2 datetime.timedelta(days=-1, seconds=86397, microseconds=735754)
The documentation states, “Note that normalization of negative calues may be surprising at first.” No kidding! Frankly, I don’t expect this to come up on Microsoft’s exam and am not inclined to spend too much time on it.
A potentially useful method of the timedelta
class is total_seconds()
, which converts a time delta value into its equivalent amount in seconds:
>>> datetime.timedelta(days=100).total_seconds() 8640000.0
timedelta
objects can take duration arguments of seven types – days, seconds, microseconds, milliseconds, minutes, hours, and weeks – but internally, these are converted and stored to only three: days, seconds, and microseconds (there are 1000 microseconds in a millisecond).
String representations of dates and times
This isn’t a particularly useful rendering for the average user:
datetime.datetime(2020, 1, 5, 9, 8, 6, 413208)
We can use either the str()
function or the isoformat()
method to represent these in a more useful way:
>>> str(datetime.datetime.now()) '2020-01-05 09:08:27.325174' >>> datetime.datetime.now().isoformat() '2020-01-05T09:08:40.381393'
For even more exact control over rendering, we can use the strftime
method combined with a plethora of various directives available. Here’s some common examples:
>>> datetime.datetime.now().strftime("%m/%d/%Y") '01/05/2020' >>> datetime.datetime.now().strftime("%A, %B %d, %Y") 'Monday, January 05, 2020' >>> datetime.datetime.now().strftime("%m/%d/%y %H:%M:%S") '01/05/20 09:16:57'
The directives themselves can be a little unintuitive and hard to remember, so consult the documentation as needed when you need to format dates in a particular way.
io
Python uses the io
module to handle various types of input and output (documentation here). This can get pretty in depth, but the most basic form was covered when dealing with files in a previous post in this series. The three generic categories of I/O that Python can handle are text I/O, binary I/O, and raw I/O:
# Easiest way to open a text stream f = open("myfile.txt", "r", encoding="utf-8") # Easiest way to open a binary stream f = open("myfile.jpg", "rb") # While rarely useful, you can technically open a raw stream by disabling buffering f = open("myfile.jpg", "rb", buffering=0)
For text and binary streams, the documentation provides alternate methods using the io
module:
import io f = io.StringIO("some initial text data") f = io.BytesIO(b"some initial binary data: \x00\x01")
Methods for the io
module include readline()
, readlines()
, close()
, and various other I/O-related functionality. This stuff can get pretty deep, but ultimately I’m not expecting much more than reading and writing to files for this section of this exam.
os
The full documentation for the os
module can be found here. While I’ve used several different methods from this module, this is the first time I’m seeing the full documentation for it. This is another module that one can absolutely go deep on, but I just plan to skim the surface here. Because I’m preparing for a Microsoft exam, only the universal or Windows-specific items matter to me here.
os.environ
Using os.environ["HOME"]
, we can access the path to our home directory. We can also access our environment variables this way. For example, I have the local path to Chrome driver stored in an environment variable named CHROME_DRIVER:
>>> os.environ["CHROME_DRIVER"] 'C:\\Users\\username\\chromedriver_win32\\chromedriver.exe'
This is also an easy and programatic way to change environment variables:
>>> os.environ["CHROME_DRIVER"] += "1" >>> os.environ["CHROME_DRIVER"] 'C:\\Users\\username\\chromedriver_win32\\chromedriver.exe1' >>> os.environ["CHROME_DRIVER"] = ( ... 'C:\\Users\\username\\chromedriver_win32\\chromedriver.exe' ... ) >>> os.environ["CHROME_DRIVER"] 'C:\\Users\\username\\chromedriver_win32\\chromedriver.exe'
Traversing directories
We can use os.chdir(path)
, os.fchdir(fd)
(which is identical to os.chdir()
, except that it takes an open file rather than a path), and os.getcwd()
should we need to traverse directories on our system as part of our program’s functionality. I’ve used os.getcwd()
(which returns the current working directory’s absolute path) most frequently of these in my programs:
>>> os.getcwd() 'C:\\Users\\username\\documents\\Python'
Another method I’ve used at various times is os.rename()
, which takes a file name and renames that file to the specified new name. There have been times where I wanted one master file in a project holding particular text, and I have often used a pattern like this:
First, open and read the file in question:
>>> import os >>> f = open("text_1.txt", "r") >>> f.read() 'I am a text file!'
Before moving on, note that if we try to rename the file now, it will fail because it is already opened by our program:
>>> os.rename("text_1.txt", "text.txt") Traceback (most recent call last): File "", line 1, in PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'text_1.txt' -> 'text.txt'
We have to close the file first:
>>> f.close() >>> os.rename("text_1.txt", "text.txt")
And now we can see that the old file name no longer exists but the new one does:
>>> f = open("text_1.txt", "r") Traceback (most recent call last): File "", line 1, in FileNotFoundError: [Errno 2] No such file or directory: 'text_1.txt' >>> f = open("text.txt", "r") >>> f.read() 'I am a text file!' >>> f.close() # don't forget to close it!
If we want to remove the file entirely, we can use os.remove()
:
>>> os.remove("text.txt")
Now if we try to open the file, we find that it no longer exists:
>>> f = open("text.txt", "r") Traceback (most recent call last): File "", line 1, in FileNotFoundError: [Errno 2] No such file or directory: 'text.txt'
Some of the commands we’re used to in the Windows command line are available in os
, such as os.mkdir()
(which makes a directory in the current location).
There are literally dozens of methods in this module that perform various functions on the system, on files and directories, processes, and more. It’s not within the scope of this post to cover close to all of it, but it is useful to know this functionality exists.
os.path
Contrary to expectation, os.path
is not just a part of the os
module – or at least, it has its own documentation separate from os
.
Regarding operating system differences, here’s a direct quote from the documentation on this:
Since different operating systems have different path name conventions, there are several versions of this module in the standard library. The
os.path
module is always the path module suitable for the operating system Python is running on, and therefore usable for local paths. However, you can also import and use the individual modules if you want to manipulate a path that is always in one of the different formats.
Again, Windows is what matters in this context. I’m going to rapid-fire list several examples of what this module can do, but I’m not going to cover every method. (Although this is, for what it’s worth, more of a manageable list of methods than the other modules covered so far.)
os.path.abspath()
returns the absolute path based on the current working directory. Assuming we’re still working in 'C:\\Users\\username\\documents\\Python'
, this would work like this:
>>> os.path.abspath("example") 'C:\\Users\\username\\documents\\Python\\example' >>> os.path.abspath("example.txt") 'C:\\Users\\username\\documents\\Python\\example.txt'
Conversely, we can get just the base file name from the full path as well using os.path.basename()
:
>>> os.path.basename('C:\\Users\\username\\documents\\Python\\example.txt') 'example.txt'
We can check for the existence of a file with os.path.exists()
:
>>> os.path.exists("example.txt") False # This is correct, I haven't actually created this file!
We can check if something is an absolute path name using os.path.isabs()
. This can be useful should one need to conditionally expand a relative path to an absolute path:
>>> os.path.isabs("example/example.txt") False
We can also use os.path.isfile()
to check if a path points to an existing file and os.path.isdir()
to check if a path points to an existing directory:
>>> os.path.isfile("example.txt") False >>> os.path.isdir("example") False
Just to confirm this works, I created an example
directory with example.txt
inside it:
>>> os.path.isdir("example") True >>> os.path.isfile("example/example.txt") True
os.path.join
intelligently joins two paths together to form a full path. This is often combining a current working directory with a relative path, like so:
>>> current = os.getcwd() >>> relative = "example/example.txt" >>> full_path = os.path.join(current, relative) 'C:\\Users\\username\\documents\\Python\\example/example.txt'
Python, of course, reads the forward slash the same as an escaped backslash, so no conversion is necessary here (although the lack of consistency does admittedly bother me a bit). We can normalize this by using os.path.normcase()
:
>>> os.path.normcase(full_path) 'c:\\users\\username\\documents\\python\\example\\example.txt' # note that it also normalized to lowercase
That’s a good number of useful examples, but there are several more worth perusing if you are working closely with paths on your operating system.
random
Finally, we have the highly useful random
module, which again, I have used quite a bit but never actually viewed the full documentation. If nothing else, preparing for this exam has helped me take a look at things I should have viewed a long time ago!
Bookkeeping for random
It’s important to note that data returned by the random
module, like most “random” computer-generated data, is actually pseudo-random, where uniform selection from a range is returned.
One can subclass random
to return a generator of one’s own devising. This starts with random.seed()
, which initializes the random number generator. If an argument is not given or is None
, this method uses the current system time to initialize its random number generator. Whether or not an argument is given, the method itself does not print anything in the Python interpreter:
>>> import random >>> random.seed() >>> random.seed(1)
Other bookkeeping functions include random.getstate()
, random.setstate()
, and random.getrandbits()
, which we won’t get into, but can further help customize random number generators.
random.random()
At its most basic level, random.random() returns a pseudo-random number (meaning the next item in its generated sequence) between 0.0 and 1.0:
>>> random.random() 0.7705231398308006 >>> random.random() 0.5396174484497788 >>> random.random() 0.8602897789205496 >>> random.random() 0.23217612806301458 >>> random.random() 0.513771663187637
Random Integers
random.randrange(stop)
and random.randrange(start, stop[, step])
return a random integer from a range (without actually building the range in memory):
>>> random.randrange(5) 1 # can return any integer from 0 to 4 >>> random.randrange(5) 4 >>> random.randrange(2, 10, 2) 2 # can return any integer from 2, 4, 6, and 8 >>> random.randrange(2, 10, 2) 6
random.randint(a, b)
is a shortcut for random.randrange(start, stop + 1)
, meaning the possible options are inclusive of the second argument given:
>>> random.randint(1, 5) 2 >>> random.randint(1, 5) 5 # see? 5 is a possiblity here!
Sequence functions
random.choice()
will return a random item from a sequence, which in Python includes lists, tuples, and strings:
>>> random.choice(["Elephant", "Baboon", "Tiger"]) 'Elephant' >>> random.choice(["Elephant", "Baboon", "Tiger"]) 'Baboon' >>> random.choice((1, 7, 99)) 99 >>> random.choice((1, 7, 99)) 1 >>> random.choice("A character from this string.") 'A' >>> random.choice("A character from this string.") 's'
We can select multiple objects from a sequence with random.choices()
with k
set equal to the number of choices we want. This function also offers optional weighting which we won’t get into.
>>> random.choices(["Elephant", "Baboon", "Tiger"], k=2) ['Tiger', 'Baboon'] >>> random.choices("A character from this string.", k=5) ['a', 'f', 'A', 'a', 'f'] # note that returned items aren't unique, there is only one "f" in the string
We can also shuffle a list in place using random.shuffle()
.
>>> list = ["Alligator", "Bat", "Cheetah", "Duck", "Emily"] >>> random.shuffle(list) >>> list ['Cheetah', 'Alligator', 'Emily', 'Duck', 'Bat']
Note that because tuples and strings are immutable, random.shuffle()
won’t work on them, but we can work around that using random.sample()
. This returns a list of a specified length with unique elements chosen from a sequence. To shuffle a full immutable sequence, just set k
equal to the length of that sequence:
>>> string = "a big fat string for shuffling" >>> new_string = random.sample(string, k=len(string)) >>> new_string ['i', 'o', 't', 'n', 't', 's', 'f', 'r', 'a', 'r', ' ', 'g', 'h', 'f', 'i', 'f', 'a', ' ', ' ', 'f', 'i', 's', 'u', 'g', 'b', 'g', ' ', 'l', ' ', 'n']
We can also, of course, choose a smaller selection from an immutable list:
>>> tuple = ("Luke", "Leia", "Darth", "Grand Moff", "Chewie") >>> selection = random.sample(tuple, k=3) >>> selection ['Chewie', 'Leia', 'Luke']
Everything else from here gets pretty abstract and mathematical, which is almost certainly beyond the scope of this exam, so I’ll close this post here.
I feel well prepared for Microsoft’s exam, but honestly, I’ll feel a lot better after acing the practice test – yes, I do indeed plan to pay for access to it prior to taking my official exam. I will likely update these posts (or add a supplementary post to this series) with anything tested heavily that I did not cover here.
I hope this series has and will help future takers of Microsoft’s Technology Associate in Python certification exam! I’d love to hear from you if so – please feel free to reach out.