Here’s how to program in Python – part 8


Until now you learned a lot of possibilities of the programming language Python. Maybe you have already started using your knowledge outside of our lessons. Great, because you learn programming by doing a lot. But a good program is also documented and well tested. You will learn that in our eighth lesson.

Here you will find lesson 1 of the Python course.

You don’t learn wrong habits quickly, which is why we now take a step back and think about what ‘good code’ is. After this lesson you will be able to produce code that is not only well documented and well tested, but you will also know how to get information about it Pythonmodules with the help function.

Document code with comments

Until now, we wrote our code without any kind of documentation. When collaborating with others on a program, it is important that you document it so that you make it clear to others what you mean by your code. But even if you program all by yourself, documentation is not an unnecessary luxury. This way you will understand your own code when you look at it again in a year.

The first way to document your code is with comments. Everything after it #character (hash) to the end of the line (unless the hash sign appears in a string) is a comment and ignored by Python. That way you can write an explanation for yourself or others. For example, if you are in the position is_palindrome of Part 6 gets confused with those indexes, feel free to add some comments:

if letters[0] ! = letters[-1]: # When the first and last letter are different

It is good style to keep two spaces between your code and the following comment and one space between the comment and your comment. Incidentally, you can also start a comment on a new separate line, then we speak of a comment line:

# When the first and last letter are different

if letters[0] ! = letters[-1]:

It is customary to comment on the line before the line it is about.

Comment out code

The comment mark (#) is also used to temporarily ‘comment out’ or turn off code that you are testing but which does not appear to work. By commenting on the code, Python ignores the code, but you can easily re-enable it afterwards by removing the comment. In this way you do not use the comment mark for documentation, but actually as an aid for debugging.

Document code with docstrings

You use comments to document why you do something and how you do it, but for users of your code is especially important what who does. To document that, you use docstrings: at the beginning of each module, class, and function or method, use a string enclosed in three double quotes to indicate what the piece of code does.

For example, we could document our classes from the previous lesson like this:

“” “Classes for working with two-dimensional, three-dimensional, and higher-dimensional points.” “”

import math

class Point:
“” “Represents a point in any number of dimensions.” “”

def __init __ (self, * coordinates):
“” “Initialize the point with the specified coordinates.” “”
self.coordinates = list (coordinates)

def __repr __ (self):
“” “Return a legible representation of the point.” “”
return ‘Point (‘ + ‘,’ .join (str (co) for co in self.coordinates) + ‘)’

def displacement (self, other_point):
“” “Return the difference between this point and another point.

The corresponding coordinates are subtracted from each other.

Example: The difference between Point (1, 4, 2) and Point (3, 4, 1) is equal to Point (2, 0, -1).
“” “
return Point (*[a-b for a, b in zip(other_point.coordinates, self.coordinates)])

def distance (self, other_point):
“” “Return the Euclidean distance between this point and another point.” “”
relative_position = self.displacement (other_point)
return math.sqrt (sum (i ** 2 for i in relative_position.coordinates))
class Point2D (Point):
“” “Represents a two-dimensional point.” “”

def __init __ (self, x = 0, y = 0):
“” “Initialize the point with the specified x and y coordinates.” “”
Point .__ init __ (self, x, y)
self.x = x
self.y = y
def get_x (self):

“” “The x coordinate of the point.” “”
return self.coordinates[0]

def set_x (self, x):
self.coordinates[0] = x

x = property (get_x, set_x)

def get_y (self):
“” “The y coordinate of the point.” “”
return self.coordinates[1]

def set_y (self, y):
self.coordinates[1] = y

y = property (get_y, set_y)

You can see here that documentation does not have to be long. The docstrings for the module, classes, and most methods are only one line long.

The most difficult method to understand is displacement and that is why it makes sense to explain it a little more. With a docstring of more than one line, we recommend keeping an empty line after the first line, and putting the three double quotes at the end on a new line. That gives a bit more overview.

Note: at our properties X and y of the class Point2D we only document the method that requests the value of the property. For example, the Python built-in help (which we will discuss at the end of this lesson) shows this documentation for the property.

Interactive examples in docstrings

We have in the docstring of the method displacement already given an example of how to use the method as documentation. But that remained rather informal. We can also do that documentation in the form of real Python code that you could enter in an interactive terminal session. Thus, the user reading the documentation can immediately copy the examples from the documentation and paste them into the Python interpreter to try them out. That would be for the method displacement look like this:

def displacement (self, other_point):
“” “Return the difference between this point and another point.

The corresponding coordinates are subtracted from each other.

>>> Point (1, 4, 2) .displacement (Point (3, 4, 1))
Point (2, 0, -1)
“” “
return Point (*[a-b for a, b in zip(other_point.coordinates, self.coordinates)])

You can see here that you have the code after the >>> exactly in a Python terminal session and get the result shown here. In fact, we just entered what it says here into Thonny’s terminal first and then copied it along with the output and pasted it into the docstring here.

Test code with doctest

Now suppose you ever code the method displacement adjusts because you think you see an improvement, but make a mistake, so the result is wrong. Then you can test that by running the sample code in your docstring in a Python terminal session. If you run the code and the result does not match the output in the docstring, your code is no longer correct.

But couldn’t we do that test automatically? Hell yes! Python knows the module for this doctest, which can automatically test all docstrings in a module for interactive examples. Here’s how to run it in a Linux or macOS terminal or the Windows Command Prompt:

python3 -m doctest -v point.py

Here is point.py the name of the module with your code in it. You will then get the tests that doctest in your docstrings and see the result. If you only want to see it when a test finds an error, get the option -v away. For example, suppose you accidentally forgot the * in the method displacement to return the list to any number of arguments to the class Point to convert, then doctest notice that error with the following message:

************************************************** ********************

File “point.py”, line 21, in point.Point.displacement

Failed example:

Point (1, 4, 2) .displacement (Point (3, 4, 1))

Expected:

Point (2, 0, -1)

Got:

Point ([2, 0, -1])

************************************************** ********************

1 items had failures:

1 or

*** Test Failed *** 1 failures.

A good way of programming is therefore that you document your code as much as possible with docstrings and also include interactive examples, which you automatically test with every change to your code. doctest to make sure you have not introduced errors.

Using the help function

If your code is documented, you don’t need to open your code file in Thonny to view the documentation. You can do this in an interactive Python session, for example in Thonny, but also in a Python session in a Linux or macOS terminal or the Windows Command Prompt. All you have to do is function Help with the name of the module, class, function, or method for which you want to see the documentation. For example in Thonny:

>>> help (Point.displacement)

Help on function displacement in module __main__:

displacement (self, other_point)

Return the difference between this point and another point.

The corresponding coordinates are subtracted from each other.

>>> Point (1, 4, 2) .displacement (Point (3, 4, 1))

Point (2, 0, -1)

By the way, all standard Python modules are extensively documented with docstrings. This makes it very easy to request documentation of all modules, classes, functions and methods in your Python terminal session. Please note that you must first import a module before you can request documentation with help.

Resume

This lesson was less about programming itself, but about documenting and testing your Python programs and figuring out more information about the standard Python modules or your own modules. The more complex your programs are, the more important these things are around your code. Therefore, make it a habit not to view documentation and tests as an afterthought, but to integrate them into your code during the development of your program. In the next lesson we will make it even more complex: we will then install additional modules that are not built into Python.

Task 1

Try the help function on your class Point2D. What could be better with the documentation shown?

Elaboration of assignment 1

>>> from point import Point2D
>>> help (Point2D)

Our class’s help function doesn’t just show the properties X and y at the Data descriptors, but also shows the methods get_x, set_x, get_y and set_y. That’s a bit too much of a good thing. Those methods do not need to be shown. We will do something about this in the next assignment.

Exercise 2

We have a property like X in the class Point2D constructed with methods so far get_x and set_x and an assignment like x = property (get_x, set_x) to use these methods together as a property. But you can also use a property with a decorator and then those methods will not be shown in the help output. Find out how to do this yourself with Python’s built-in help.

Elaboration of assignment 2

With help (property) you will receive an explanation about the assignment property. You even learn that it is not an assignment but a class. You can apply the example shown in the help output almost directly to our class Point2D. Then you come to the following code X:

@property
def x (self):
“” “The x coordinate of the point.” “”
return self.coordinates[0]
@ x.setter
def x (self, x):
self.coordinates[0] = x

Do the same for it y. Run the help function on your class again and verify that you are only getting help on the properties and not on the methods that build the properties.

Cheat sheet

Comment: a line that starts with # and serves as an explanation for a piece of code.

Debugging: detect and fix errors (bugs) in your code.

Docstring: a string enclosed in three double quotation marks that documents a module, class, function, or method.

Comment out: Disable a line of code by commenting it out.

.

Recent Articles

Related Stories