Python Worksheet 0: How to actually run it

8 Sep 2023, by Aadi

Back to all sheets

Before we go deep into how to write programmes, we will look at the many ways we can actually run it. This will not only help us learn better by allowing us to experiment in different ways, but it will actually make this new skill more useful – because there will be more places to apply it.

1. The Python interpreter

This is the most basic way to run Python. Just open whichever terminal you have, and type in

$ python3

and you should be greeted with a version number, along with some other details like so:

Python 3.11.4 (main, Jun 20 2023, 16:59:59) [Clang 14.0.3 (clang-1403.] on darwin
Type "help", "copyright", "credits" or "license" for more information.

Don’t fail to notice the hint given at the end: Type “help” for more info.

The cursor must be blinking at us, in front of three > symbols. This >>> is called a prompt. It’s prompting us for input. We can type any valid python here, and press Enter to execute it.

This way we can do whatever we want, it’s not limited in any way. In a sense, this is all that there is to Python.

Say we declare some variables,

>>> a = 1
>>> b = 2
>>> c = a + b
>>> print(c)

Now we have some variables in our state.

If we then type exit() or press Ctrl+D then we exit Python, and those variables are gone. Typing python3 again and then entering print(c) will no longer print 3. It will instead complain that it doesn’t know anything called c.

I encourage you to try it, just for the sake of getting familiar with that error message at least. It’s worth it.

So that’s the plain, classic Python interpreter. We can press the up and down arrow keys to navigate through previous commands, left and right arrow keys to navigate the command.

The main uses of this interpreter for most people is that it’s a really easy way to test out parts of bigger programmes, or just try something out quickly. Similarly, it’s is useful to check whether a command works as well.

For example, if we’ve installed the package numpy and want to check if it actually has worked, then we can easily run python3 and type in

import numpy

If this command result in an error, then we know that Python found a package called numpy and was able to import it, ready for us to use.

2. Python scripts

Say I use numpy to invert matrices. It goes something like this:

>>> import numpy as np
>>> A = np.array([
... [2,0],
... [0,2]
... ])
>>> np.linalg.inv(A)
array([[0.5, 0. ],
       [0. , 0.5]])

Isn’t that cool? Say every time I want to invert my matrix, I don’t want to type out all the commands again. Woudn’t it be nice to be able to store those commands in a file, and then ask Python to just run those commands line by line, as if we had typed them?

Let’s see what happens. Open any text editor, then type out those commands exactly as we typed them before (don’t type any of the output it printed of course, just the input we gave it) and then save that file. It need not have a .py extension. As long as that file is readable, it doesn’t matter what extension it has, but it’s extremely convenient to be able to quickly identify that this is a Python file because of its .py extension.

Our file should look something like this:

import numpy as np
A = np.array([[2,0], [0,2]])


Suppose this file is saved as in the current directory. Then running python3 in the terminal should work.

Try it. What do you see? Nothing? Oh, right. We didn’t ask it to print anything.

That’s a notable difference between running in the interactive mode or running files. In the interactive mode, the return value of the function is printed out by default. On the other hand, while running the same commands through a file, the result of every expression isn’t printed out unless we ask it to.

And that makes sense. This is a tiny file. But bigger files have hundreds if not thousands of lines. If each line printed something out, it wouldn’t be nice. What we wanted would get lost in the streaming mess.

Can you modify the file so that it prints the inverse of A?

3. Modules

In the above examples, we imported numpy as np. This was because we wanted to be able to use the features of the numpy package. It comes with useful data types like numpy.array and useful functions like numpy.linalg.inv.

We can make packages of our own! When it’s a single file, it’s usually called a module, while a package is a collection of modules. In the underlying technology, there’s not much of a difference.

Create a new file with the following contents

print("Welcome to my module!")
pi = 3.141597

def perimeter(radius):
  return  2 * pi * radius
print("You've reached the end of my module")

cd to the directory where the file from the earlier section is saved, and run python3 to launch the python interpreter. Now in the interpreter, type import file. If your file is named you need to say import foo.

What do you see? The two sentences are printed out but that’s not all that has happened. The interpreter has gone through the file – all the definitons.

We can verify this by trying out print(foo.pi). Again, if your file is not called then you will have to modify the above command aaccordingly. (It’s good practice to have meaningful names – it takes very little time, and saves a ton.)

Now try foo.perimeter(2). It should say a number close to 4π.

What is going on here is that import foo will run the file called (if it exists) and remember all the definitions within a separate namespace, called foo. That’s why just perimiter(2) doesnt work, but foo.perimeter(2)` does.

Separate namespaces are really important for avoiding conflicts. Imagine a huge programme that imports tens of modules. It’s likely that two different modules have the functions with the same name. For example, the module math has a function called sin and so does numpy.

importing both still works, because math’s sin is called by math.sin() and numpys sin is called by numpy.sin().

So that’s another way of running Python programmes: through other programmes. But there’s two issues.

One is that I don’t want to have to type the name of the module every time I call its function, or access its variables. That’s easy to fix, and we know it already! import numpy as np now lets us access all Numpy functions through np.function() instead of numpy.function(). Same works for variables/constants like np.pi, of course.

The second issue is a little more complicated. Whenever I am importing our tiny perimiter module, I get two lines of output. That’s really not ideal. Some people who are using our module won’t like this output interfering with their programmes’ own output. But say I don’t want to remove those two print statements from my programme. I want to keep them if I run the programme on it’s own. Yes, this is not the ideal example, but trust me on this. Sometimes we want a module to behave differently when it’s run on it’s own, and differently when it’s run because of an import.

This can be checked by looking at the value of __name__ like so:

if __name__ == "__main__":
  print("Welcome to my module!")
pi = 3.141597

def perimeter(radius):
  return  2 * pi * radius

if __name__ == "__main__":
  print("You've reached the end of my module")

Save this as a file. Now run python3 and see whether it is different from import foo.

There we have it! Three different ways to run python programmes. In fact, we created our own module! That’s going to be handy soon, I hope!