2.1. Hello world

A typical first task in any programming language when starting to learn to program in that language is to get the string “Hello world!” printed out.

What sounds like a boring idea is actually quite interesting, as it shows many general aspects of a programming language. Successfully completing this task proves that you can write and run a (minimal) program.

Furthermore, we will use the simple idea of printing out “Hello world!” to briefly introduce a whole list of concepts, such as functions, classes (object-oriented programming), Python packages, and eventually even (unit) tests. There are two reasons for doing this using the “Hello world!” program and that early on: (i) the “Hello world!” program is so simple that we can focus on the other concepts, and (ii) looking into organising your code (functions, classes, packages) and writing tests helps you getting your priorities right straight from the beginning (i.e.: readability, reliability, maintainability).

2.1.1. Command line

Our task is to use the Python command line to print the string “Hello world!” for us. Therefore, we first need to start the Python interpreter, i.e. the Python command line.

Open a terminal on your computer and type in the name of your Python interpreter. Usually, this is something like python, python3 or, using Windows, py.

After having started the Python interpreter, it should greet you with a message similar to the following (this message is from typing python3 into a terminal on my MacBook running macOS 10.14):

Python 3.7.7 (default, Mar 10 2020, 15:43:03)
[Clang 11.0.0 (clang-1100.0.33.17)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

The three angle brackets, >>>, are the command line prompt where you can enter your commands directly.

To make Python print out the string “Hello world!”, type in the following command:

print("Hello world!")

The result will look like follows:

Hello world!
>>>

Note that I’ve given the prompt, >>>, explicitly here.

So what did we do here? We’ve told the Python interpreter to print out a text message (a “string”), using our first Python command, namely print(). We know that “print” is a function, as it is followed by brackets that enclose its “argument”, namely the text message we want it to print. To tell the print() function that its argument is actually a text message, we used the quotation marks. As we will see later on, it can become quite important to distinguish between the “type” of an argument, be it text or a number or something else.

Exercise

What would actually happen if we would use single quotes instead of double quotes? Try it out by yourself.

Now, after having played with the Python command line and make it print “Hello world!” for us, it would be great to know how to leave it again. There are different ways of leaving the Python command line, typing exit or quit are just two of them:

>>> exit()
foo@foobar:~$

Here, foo@foobar:~$ is the prompt of my terminal (actually, it is not, but it looks similar). This just tells us that we have exited the Python command line. The same can be achieved using quit:

>>> quit()
foo@foobar:~$

There is, however, even a third way, namely using the keyboard shortcut Ctrl + d. This does the same, immediately exiting the Python command line. The result will look like this:

>>>
foo@foobar:~$

Note that in this case, no command appears on the prompt of the Python command line, but you see that we’ve returned to the terminal prompt.

2.1.2. Script

While using the command line may be helpful from time to time, there are pretty many pretty good reasons to write the commands in a file and afterwards execute this file (namely, call Python and give it this file to execute).

To do so, open a text editor of your choice, and simply put in the same line we’ve typed into the Python command line before:

print("Hello world!")

Afterwards, save this file under the name hello.py.

Note

It would be a good idea to first create a directory for this Python course, inside it a directory started, and in there this file hello.py. This way, you learn (or at least train) to organise your files, and afterwards you know a bit better what you’ve done.

Bonus: After you’ve created the directory, make it a git repository. From now on, whenever you’ve created or changed a file and finished a task, make a commit, i.e. add a new version to your version history. For more details on how to work with git, see the chapter on version control.

Now how to call this file and execute its contents? From the terminal it is as simple as using the command python3 (or similar), followed by the file name:

python3 hello.py

The complete terminal output should then look somewhat similar to the following:

foo@foobar:~$ python3 hello.py
Hello world!

Note that foo@foobar:~$ is again the prompt of my terminal.

Sometimes, it can be quite convenient to have such a script executable. In the unixoid world (fortunately, Linux and macOS), this can be achieved quite easily. All you need to add to your script is a first line telling it where to find the Python interpreter on your system. For me, this looks like the following:

#!/usr/local/bin/python3
print("Hello world!")

Type this into your text editor and save it as hello2.py in the same directory as before. But what’s this first line good for? The #! is a specialty of scripts in the unixoid world, called a “shebang”. Following straight after it is the path to the executable, in our case the Python 3 interpreter. The exact path may vary from installation to installation. If you would like to know it for your system, try something like:

which python3

The result should be a path like the one I’ve shown you above. In order to make the file executable, you need to change its permissions, i.e., set the “executable” flag. In the unixoid world, this is done like so:

chmod +x hello2.py

Afterwards, you are able to directly execute the file hello2.py by typing its name into the terminal:

./hello2.py

The ./ at the beginning is another specialty of the unixoid world, telling the operating system to execute a file in the local directory. Usually, executables need to be located in special directories (a basic, but very useful aspect of security). To make a long story short, the result should be familiar:

Hello world!

Again, just for clarity, here the full output of my terminal:

foo@foobar:~$ ./hello2.py
Hello world!

Quite impressive what you can do with a simple terminal, isn’t it? To dig deeper into the power of the unixoid terminal, you would need to consult good books (that are available in great numbers) or use your preferred web search engine.

2.1.3. Function

Scripts are great for trying out things, however, a basic though very useful first line of organisation in your code are functions. Therefore, let’s see how we can use functions in Python to print “Hello world!” to the command line. A very simple function may look like follows:

def hello():
    print("Hello world!")

Type this into your text editor and save it as hello3.py in the same directory as before. So what did we do? We’ve defined our first function. The key to functions is the keyword def, followed by the function name – with optional arguments enclosed in brackets. Important as well is the trailing colon, :. The actual “body” of the function is indented by four spaces (don’t use tabs, but spaces, and use four and only four spaces).

Next, you may try to call this file similar to before:

python3 hello3.py

However, the result is not quite as we expected: Simply nothing is printed. But why is that? Simply defining a function doesn’t actually call the function. Therefore, we need to add a function call at the end of the script, like that:

def hello():
    print("Hello world!")


hello()

If you call this file now, same as before:

python3 hello3.py

it will actually print the string “Hello world!” on the command line. Hence, mission accomplished. Congratulations, you’ve actually written and executed your first Python program.

Just one last remark on what we did here: We’ve added a call to the function to the very end of our Python program. While this works as expected in the current context, if we would import our module hello3 in some other file, we would always get an output on the terminal during import. Hence, we would like to have a way to only get the function called when we execute the file hello3.py directly, not when we import it into another Python file. To do so, let’s modify our Python program a last time for now:

def hello():
    print("Hello world!")


if __name__ == '__main__':
    hello()

For didactic reason, you may save it as hello4.py in the same directory as before. The slightly weird condition at the end does the trick. So what’s actually happening here? We are testing for the content of the special variable __name__ that gives us the current context of the caller. If its content is __main__, we got called by the Python interpreter directly, and in this and only this case, we call the function. You will find this pattern from time to time in Python programs, and it is quite convenient sometimes for some testing and debugging.

Just to make sure everything works as expected, here is again the full output of my terminal when calling hello4.py directly:

foo@foobar:~$ python3 hello4.py
Hello world!

As we did actually execute the entire code of the file directly, the context of the caller is __main__ and hence the function is called and the string “Hello world!” printed.

2.1.4. Class

Can we drive it even further? Yes, we can! While functions are useful first line of organisation in our code, we can introduce classes and hence concepts of object-oriented programming (OOP). Don’t worry, we will introduce all these concepts more rigorously and in some more detail later. For now, let us just look at how a simple class allowing us to print “Hello world!” may look like:

 1class Hello:
 2
 3    def __init__(self):
 4        self.greeting = "Hello world!"
 5
 6    def hello(self):
 7        print(self.greeting)
 8
 9
10if __name__ == '__main__':
11    say = Hello()
12    say.hello()

You may want to create an new file hello5.py and populate it with the contents of the above listing. Of course you can copy and paste, but actually typing these few lines will help you getting familiar with coding.

So what happened? Let’s look at the familiar things first: Our function hello() (in lines 6 and 7) didn’t change much. It got an argument self, though, and we will come to it in a minute. Just to get terminology right straight from the beginning: functions of classes are usually termed “methods”, and variables that belong to a class are termed “properties” of that class. Furthermore, a class is a mere blueprint of the instance you are actually working with and that does the real job, and this instance is called an “object”.

To start from the beginning, we defined a class using the keyword class (line 1). Note that Python has some clear rules for naming different things. Class names follow CamelCase, while functions and methods (as well as variables) use underscores to separate words in a name and only small caps.

The weird-looking method __init__() is a “constructor”, its name is fixed, and it will be called when instantiating an object of the class, preparing all necessary things for you. Hence, it is an excellent place to define properties of the class, as in our case the property greeting. Why the prefix self? Simply to refer to the class/object itself. Hence, usually every method of a class will have self as a first argument. So what did we do here? We set a class property greeting to the string “Hello world!”, and we can access this property later in other methods of the same class.

Now hopefully it becomes a bit clearer what happened to our previously innocent function hello() from above. As it is a regular method of the class Hello, it takes an argument self. And thus it can access the class property greeting, as shown in line 7.

All well and good, you may say, we now have a class. But how to actually use that beast? Line 10 should look familiar by now, as it just prevents the following code from being executed if you import the module. The key to using classes is shown in the following lines 11 and 12. Actually, it is always (at least) a two-step process: (i) create an object of a class (line 11), and (ii) call some method of the object just created (line 12). Let’s have a look at these two lines again in more detail:

say = Hello()
say.hello()

With the first line, we create an object of the class Hello and name it say. Sometimes, creating an object of a class is called “instantiating”, as you create an instance of the class. As said above, classes are the blueprints, and objects are the real things that do the actual work during runtime of your code.

The second line is simply the call of the method hello() of your object say (of class Hello). Now you may understand why we’ve named our object say, as “say hello” is pretty readable code that we can understand without knowing how to program.

Is it worth all the fuss, you may ask. The simple answer: for just printing “Hello world!” to the terminal surely not. The reason for stretching the “Hello world!” example so far here is that it helps you concentrate on the strategies, as printing “Hello world!” is a real no-brainer for you.

Again, lets make sure everything works as expected:

foo@foobar:~$ python3 hello5.py
Hello world!

No surprise here as well, we managed to get “Hello world!” printed to the terminal using classes and objects.

2.1.5. Package

Let’s see how far we have gone by now: We started with the Python command line, created our first script, a function, and finally a class, all simply printing “Hello world!” to the terminal/command line. So what is next? Whenever we do some programming, we want to be able to (re)use our code as simple as possible, and we want others to be able to (re)use our code as well. So how about creating a Python package from our code, allowing us (and others) to install it locally and use its functions and classes in our code?

One of the great things of the Python ecosystem (as well as with some other programming languages) is a well-developed strategy for creating and distributing your code in terms of packages. The first steps described here lead you all the way to creating packages that you can distribute via the Python Package Index (PyPI), although we can and will cover only the very first steps in that direction here. For more details, see the respective section of the course.

2.1.5.1. Basic directory layout

What makes a Python package a Python package? Actually a bit of file organisation in conjunction with a few special files. While there are different ways to create a Python package, we will use one distinct way here that works well in many cases. The basic directory layout of a Python package is shown below:

mypackage/
├── mypackage
│   ├── __init__.py
│   └── module.py
└── setup.py

Note that this is pretty much the minimal setup of a Python package. So what is special about it? All the code (i.e., the modules) reside in a subdirectory (with the same name as the actual package, here “mypackage”) that contains a (by default empty) special file __init__.py next to the other modules. Furthermore, we need a file setup.py in the root directory of the package. Again very minimalistic, the contents of the file setup.py could be as simple as

import setuptools


setuptools.setup(
    name='mypackage',
)

Important

Please, bear in mind that the example shown here is purposefully minimalistic and not meant to be used in production. For any actual package, you would need to provide a lot more information, at the very least an author name, a license, a version number, and a description.

2.1.5.2. A helloworld package

Let’s apply our newly acquired knowledge to our “Hello world!” program, and for reasons that will become obvious later, we will use the OOP version (with the class). Suppose that you’ve followed along the course so far, created a virtual environment and a directory for the source code created during this course and in there a directory called “started” (for this first part of the course). The next steps would be to crate a directory “helloworld” for the package we are about to create, and inside this directory, create the following file structure:

helloworld/
├── helloworld
│   ├── __init__.py
│   └── hello.py
└── setup.py

Starting with the obvious things, the contents of the module hello.py within the helloworld package are basically identical to the file hello5.py, except that in this case, I left out the if __name__ == '__main__': construct at the bottom. To be explicit, the module (file) hello.py in our package helloworld looks like that:

class Hello:

    def __init__(self):
        self.greeting = "Hello world!"

    def hello(self):
        print(self.greeting)

As mentioned, the file __init__.py is empty (and will remain like that for the time being in many cases).

The only thing that is left is the file setup.py in the root directory of our newly created package. In line with the purposefully minimalistic example given above, it looks like this:

import setuptools


setuptools.setup(
    name='helloworld',
)

With that, we are settled to install our package. Remember to create a virtual environment for this purpose, if not already done, as otherwise, you will clutter your Python installation. Albeit you can uninstall packages, this is one of the things that will usually be forgotten about, as it requires keeping track of what you have done and getting back to it.

Note

Just for convenience, and for those who have not done it yet, this is how to create a virtual environment for working along the course:

python3 -m venv python-course

This will create a virtual environment in the directory “python-course” in the current directory. To activate it (assuming a unixoid operating system), use:

source ./python-course/bin/activate

Using Windows, the same should be achieved using:

.\python-course\bin\activate

In case you would like to leave the virtual environment, either use the command deactivate in the terminal or simply close the terminal window.

Once you’ve activated your virtual environment, navigate to the root directory of your newly created helloworld Python package (the directory the file setup.py is located in) and issue the following command:

pip install -e .

This will install your package locally in an editable fashion (hence the -e switch) and make it available for further use. The output of the above command should look similar to the following:

Obtaining file:///<some path>/python-course/started/helloworld
  Preparing metadata (setup.py) ... done
Installing collected packages: helloworld
  Running setup.py develop for helloworld
Successfully installed helloworld-0.0.0

Hooray! You’ve (presumably) installed your first (own) Python package. What does this help us now? We can use the package as any other Python package and import it into our scripts and modules – and we can much easier continue with writing tests and do test-driven development, but for that, see the next section below.

Note

From the terminal output shown above, you can immediately spot one problem with our minimalistic setup.py file: As we have not provided any version number, it is just assumed to be 0.0.0, and that is not very helpful, as version numbers are used, inter alia, to check for newer versions and to install them if necessary.

Finally, having crated and installed our package, we want to use it, i.e. import it and work with the functionality it provides. To do that, let’s just create a file hello_from_package.py outside our package, e.g. in the python-course/started directory where all the other files from the previous exercises are residing, and put the following content in it:

from helloworld.hello import Hello


say = Hello()
say.hello()

So what happens here? The first line is the crucial one, as the two lines at the bottom are nothing new for us, just instantiating an object of the class and calling its method.

We have used a particular version of the import statement here, importing directly just one “symbol” of our module hello from our package helloworld, namely the class Hello. You could have been more explicit in your code with regard to the origin of the symbols you use, writing instead something like:

import helloworld.hello


say = helloworld.hello.Hello()
say.hello()

Often, this more explicit variant is the preferable, as it makes it explicit in the code where certain parts belong to or originate from, although to some extent it is a matter of taste. We will deal with some details later.

Note

If you’re interested in more details of how to create Python packages and don’t want to do it all manually and by yourself, you may want to have a look at the pymetacode package and in particular its documentation available online. This will not only help you create the basic layout of a package, but as well with maintaining its structure and a certain level of consistency during further development.

2.1.6. Tests

Now that we have a package, we can simply extend it and start writing tests for our functionality. Thus, we do two things: extend the structure of our package by adding a tests directory, and create a (test) module for our hello module that tests the Hello class.

First things first, after adding the tests directory and the test_hello.py module, our package structure should look like this:

helloworld/
├── helloworld
│   ├── __init__.py
│   └── hello.py
├── tests
│   └── test_hello.py
└── setup.py

Next is the contents of our module test_hello.py containing the actual (unit) tests. A plain test module (as created, e.g., by PyCharm if you ask for it), looks like this:

 1import unittest
 2
 3
 4class MyTestCase(unittest.TestCase):
 5    def test_something(self):
 6        self.assertEqual(True, False)  # add assertion here
 7
 8
 9if __name__ == '__main__':
10    unittest.main()

This includes already the basic scaffold of tests using the unittest framework provided by Python. A few basic things to note:

  • Import the module unittest at the top, followed in real code by importing your module you want to test.

  • Define classes for test cases that inherit from unittest.TestCase (as shown).

  • Define methods for the individual tests, each prefixed with test_ it its name (note that this is a convention you need to follow in order to have the tests run).

What can we test in our case of the Hello class? We could basically test two things:

  • The class can actually be instantiated, i.e. it exists.

  • Calling Hello.hello() prints the string “Hello world!” to the standard output.

As we will see, the latter, as simple and innocent as it may sound, is rather tricky, and the problem this method provides us when we are about to test it hints at problems with how the method is written in the first place. This is a nice example of how writing tests first (or at least early on) will lead to more testable code and generally to better design and higher code quality. But these are topics for later. Now, let’s look at the actual tests for our helloworld package:

 1import contextlib
 2from io import StringIO
 3import unittest
 4
 5from helloworld.hello import Hello
 6
 7
 8class TestHello(unittest.TestCase):
 9    def test_instantiate_class(self):
10        Hello()
11
12    def test_say_hello(self):
13        temp_stdout = StringIO()
14        with contextlib.redirect_stdout(temp_stdout):
15            say = Hello()
16            say.hello()
17        output = temp_stdout.getvalue().strip()
18        self.assertEqual("Hello world!", output)
19
20
21if __name__ == '__main__':
22    unittest.main()

As you can see, the basic structure is identical to the template shown above. However, as we need to capture the output of the hello() method, we need to redirect what is called the “standard output” (or stdout in short). But let’s start simple. The first method in our test class simply tests whether we can instantiate an object of the class under test. Hence, no “assert” statement here. While this is usually the first test I write when starting to create a class, you can safely delete it afterwards (or entirely omit it).

The second test is a bit more involved, as mentioned, as we need to redirect stdout to a temporary variable that we can use in our test, i.e. the assert statement in line 18.

Note

What you see here is a typical side effect of our method hello(): it prints to stdout, something often done for debugging purposes (though there are much better ways to do that, such as proper logging). While in this particular case, the side effect is the intended result, usually you want your code not to have such side effects (i.e., changing the state of other, unrelated parts).

Being a rather untypical example test-wise, usually your functions will return some value that you can directly assign to a variable and test that conveniently, or a method of a class will change the contents of some of the class properties that you can often check for easily.

To sum it up, what do tests help us with? As mentioned, code that is designed to be tested from the beginning is often more modular, better designed and generally of better quality. Furthermore, thinking in terms of tests before you write production code makes you think about how you would like to call your functions/methods or more generally use the interfaces you define. Therefore:

  • Write tests first, before you write the production code, assuming the production code to exist already and calling it exactly as you imagine it. This allows you to get immediate feedback on your interfaces (i.e., function and method declarations).

  • If you have some (untested) code you need to make changes to, try to write tests for your new functionality, or in case you need to debug it, try to write tests for behaviour that you expect from your code. Admittedly, bringing untested (“legacy”) code under tests is often a difficult matter and the topic of whole books.

While often tests are not dealt with until very late in introductions to programming, testing your code is an essential aspect, particularly in (but not limited to) the context of scientific programming and software development. Having tests doesn’t mean at all that your code does not contain bugs (it certainly will do). But it does mean that you have some specification you can automatically check against. This means that if you make changes to your code, chances are that you detect if something went wrong. Without tests, you will never know unless you figure out by using the software yourself or getting feedback from its users.

2.1.7. Wrap-up

What did we achieve so far? Pretty much, given that we started with what is considered (one of) the most primitive problem(s) in every programming language, namely displaying the string “Hello world!” and ended with classes, packages, and unit tests. The reason for doing all this with a simple “Hello world!” program is that the actual functionality is so simple that it does not distract us from understanding the principles introduced.

There is two things, however, we did not address here at all and that are crucial for every serious programming and software development: documentation and version control. We will address these topics in later chapters of the course.

But now it is really time to look into some language fundamentals.