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.
Today’s topics are a mix of different things, many of which I’ve been using throughout this series:
Document code segments using comments and documentation strings
This sounds simple enough, but there’s actually an item here I’m not at all familiar with. We’ll get to that.
First, let me link multiple style guides that every Python developer should be familiar with, as these seem most relevant for these topics:
- PEP 8 – Style Guide for Python Code – This is the Official Style Guide and you will hear about PEP 8 standards frequently as a Python dev.
- Google Python Style Guide – If it’s good enough for Google, it’s worth at least reading through to me.
- Code Style — The Hitchhiker’s Guide to Python – Perhaps a little less comprehensive, but arguably more readable than the first two.
Use indentation
Indentation is key in Python, as Python depends on code blocks adhering to correct indentation standards to actually run a script.
You can use either two-space or four-space indentation, but what really matters is consistency. There is debate about whether one should type two/four spaces or just use the tab key, which is relevant if you’re using IDLE or maybe something else, but the best answer is to use an editor that converts tabs to spaces for you. If you are using IDLE and the distinction matters, spaces are probably strictly better, but again, the real thing to focus on is consistency.
Nearly every code example in this series shows off Pythonic indentation, so check those out.
Use white space
Sparse is better than dense.
–Tim Peters, The Zen of Python
That’s largely going to be my summary of the proper use of white space, although I’ll own up to having trouble with this sometimes. I just really like tight, compact, dense code that does a thing in just a few lines, but even I admit that a well-placed empty line can really group one’s code together.
The PEP 8 notes on whitespace include a bunch of don’ts that thankfully I don’t engage in, except that I’ve picked up a habit of using the colons in a dictionary with the same syntax as you would use with an equal sign, with a space on either side:
danny = { "likes_it" : "this_way", "it_probably": "should_be_like_this" }
What can I say? Every other pet peeve they list I agree with wholeheartedly, but man, I just think the first key-value assignment above looks better. I do not apologize or intend to reform this part of my code. But I will be aware of it for testing purposes should it be clear they’re trying to catch me in that preference.
Use comments
Comment syntax is basic stuff:
# this is a single-line comment """ this is a multi- line comment """
Writing well-commented code isn’t just a favor to other developers, it’s a favor to future you. Take care of yourself and take the time to write a little explanation up as you work. You won’t enjoy it when it’s months or years later and you have no idea what code – that you wrote! – even does.
Use documentation strings
A docstring should be added as the top line(s) of a function enclosed in triple quotation marks. Docstrings should state what a function does, not how it does it. Here’s a great article breaking down docstrings and their usage. That article provides some really nice rules to abide by for writing doc strings which I will quote here:
- The doc string line should begin with a capital letter and end with a period.
- The first line should be a short description.
- If there are more lines in the documentation string, the second line should be blank, visually separating the summary from the rest of the description.
- The following lines should be one or more paragraphs describing the object’s calling conventions, its side effects, etc.
Here’s a couple examples for value:
def simple_function(): """ This function does something simple. """ print("simple")
def more_complex_function(*args): """ This function does a thing that is fairly complex. It does this thing through various methods, which are listed here. It has side effects of various types, such as doing awesome stuff that you designed it to do. It accepts arguments of some types that are being described here. """ do_a_complex_thing()
You can access your docstring in two different ways. The first is help()
:
>>> help(simple_function) Help on function simple_function in module __main__: simple_function() This function does something simple.
The second is __doc__
:
>>> simple_function.__doc__ ' This function does something simple. '
Generate documentation using pydoc
This is the thing I mentioned up top that I wasn’t familiar with, but I took some time from this post and got there. Here’s the full documentation on the built-in pydoc
module.
The help()
function above uses the pydoc module to grab documentation strings from a function, but it can also be applied to entire modules. I decided to try this out on a Python class I’m actually working with these days, and it’s pretty cool! It grabs the docstring for the class, but also each method’s name, signature, and docstring, as well as some other information.
Start with this command in your terminal:
λ python -m pydoc selenium_helper
And then here’s the output:
Help on module selenium_helper: NAME selenium_helper CLASSES builtins.object SeleniumHelper class SeleniumHelper(builtins.object) | A parent class that assists with Selenium testing and automation. | | This class is built to be inherited by more specialized tasks that | will benefit from the Selenium web driver functionality within. | | Methods are arranged alphabetically for value. | | Methods defined here: | | attach_image_file_to_input(self, input_id, img_path) | Method to attach an image file to input with type="file". | | Requires a string partial/unique id of an input element and | the path of an image file (relative to the working directory), | then attaches the image to the input element. | | check_box(self, checkbox_id, uncheck=False, return_status=False) | Method defaults to checking unchecked boxes. | | Requires a string partial/unique id or XPath. | | * Optional arguments/behavior * | uncheck=True -- will instead uncheck a checked box. | return_status=True -- instead returns checked status | | clear_input_element(self, element_id) | Method to clear an input element. | | Requires a string partial/unique id or XPath. | | click_button(self, identifier, dbl_click=False, no_js=False) | Method to click an element (with JavaScript by default). | | Requires a string identifier that can be a partial/unique | id, CSS selector, or XPath. | | * Optional arguments/behavior * | dbl_click=True -- double clicks the element. | no_js=True -- mimic user click rather than click with JS | | get_text_from_element(self, element_id, input=False) | Method returns text from an element. | | Requires a string identifier that can be a partial/unique | id, CSS selector, or XPath. | | If trying to get the current text/value from an input | element, include `input=True` to do so. | | set_input_elements(self, data) | Method to set any number of text input elements on a page. | | Requires a dictionary with at least one key-value pair but | can be any size. Keys must be an identifier that can be a | partial/unique id or XPath. Values should be the desired | text corresponding to each identifier. | | set_select_elements(self, data) | Method to choose any number of select elements on a page. | | Requires a dictionary with at least one key-value pair but | can be any size. Keys must be an identifier that can be a | partial/unique ~name~. Values should be the text of the | desired option to select. | | start_chrome_driver(self) | Method to start the Chrome driver with specified options. | | start_firefox_driver(self) | Method to start the Firefox driver with specified optons. | | start_ie_driver(self) | Method to start the Internet Explorer driver. | | switch_to_iframe(self, iframe_id) | Method to switch to an iframe on a page. | | Requires a string identifier that can be a partial/unique | id or Xpath. | | wait_for_page_load(self, timeout=10) | Method to wait for page load before continuing. | | This method checks for the staleness of the old page | (i.e., that the new page has loaded) prior to moving | forward with further actions. | | Thanks to ObeyTheTestingGoat for this delightfully | borrowed method! | | Usage: | | with self.wait_for_page_load(): | # click a button or do whatever | # do the next thing that was failing before using this | | ---------------------------------------------------------------------- | Data descriptors defined here: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined) FILE c:\local\path\to\module\selenium_helper.py
Construct and analyze code segments that include function definitions
We’ve covered a number of function definitions so far, but some of the details below are worth getting into in more depth.
Call signatures
A function signature includes the arguments that are defined in between the parentheses of the function definition. Consider the following code:
def main(something): print(f"I'm a beautiful {something}! ✨")
Here, the signature of this function is (something)
. We can inspect the signature by using the following:
import inspect def main(something): print(f"I'm a beautiful {something}! ✨") if __name__ == "__main__": print(inspect.signature(main))
output
(something)
I’ve never had to use this in practice, but I can vaguely imagine scenarios where it would be useful to examine the required arguments for a function prior to taking action with it.
I will note that Microsoft calling these “call signatures” is throwing me a little bit, as I’m not finding much on that term and am assuming they mean “function signatures” (as found in this article on functions). That said, googling both terms is not providing me with anything distinctly different, so I’m hopeful that it’s just an unusual synonym.
Default values
The day that default values clicked for me was a big level-up with my Python code. Default values can allow your function to do so many more things without adding a ton of baggage in the form of extra arguments for each call.
We can expand our last example a bit:
def main(something="function"): print(f"I'm a beautiful {something}! ✨") if __name__ == "__main__": main()
output
I'm a beautiful function! ✨
That’s cool and everything, but the beauty of it is that – if and only if we want to – we can define the term as something else.
def main(something="function"): print(f"I'm a beautiful {something}! ✨") if __name__ == "__main__": main("synthesizer") # dream big, little guy
output
I'm a beautiful synthesizer! ✨
We can have as many default arguments as we want:
def main(something="function", thing="does awesome stuff"): print(f"I'm a beautiful {something} that {thing}! ✨") if __name__ == "__main__": main()
output
I'm a beautiful function that does awesome stuff! ✨
If we order our arguments in the same order, we can just send them as normal:
main("synthesizer", "makes the greatest beeps and boops")
But we can also specify the variable an argument should be assigned to, especially if we’re not sending all of our arguments or are sending them out of order:
def main(something="function", thing="does awesome stuff"): print(f"I'm a beautiful {something} that {thing}! ✨") if __name__ == "__main__": main(thing="makes the greatest beeps and boops")
output
I'm a beautiful function that makes the greatest beeps and boops! ✨
If it’s not clear, I’m a really big fan of default arguments and how much flexibility they give to a programmer. It’s definitely worth getting comfortable with them as you develop your Python expertise.
The def
, return
, and pass
keywords
All of these have been used multiple times throughout this series (and even this post) so far, and are frankly just basic computer science concepts. I’ll go over them quickly.
def
def
is the keyword reserved for defining functions and methods. My last several examples in this post show def
in action.
return
return
is a keyword reserved to close a function out, and if a value is specified, that value is sent back to where the function was called, likely to be stored in a variable or checked in an expression. A simple example function with a return statement would be:
def square(x): return x**2 if __name__ == "__main__": a = square(2) b = square(3) c = square(4) print(a, b, c)
output
4 9 16
pass
As I stated in a past post, my primary usage for the pass
keyword is just to act as a placeholder for an indented segment of code when I want to compile a script:
def function_name(): pass if expression: pass for item in items: pass while True: pass
And so forth.
Googling around to find if there is another usage, I instead found some different definitions that help me describe the keyword in more formal terms: pass
is used when code is syntactically required but you don’t actually want anything to be executed.
I’m eager to take this exam, but I have two more review posts to write up first. Hopefully, I can churn those out and get certified soon!