Part Nine: Python Files and Modules
Tags: coding, python, programming, tutorial
Published: 2022-12-10 18:24:29

Welcome to part nine. In this series I hope to introduce the basics of coding in Python for absolute beginners in an easy to follow and hopefully fun way. In this article we will look at the basics of importing and using modules in our code.

---

> "But while you can always write 'spaghetti code' in a procedural language, object-oriented languages used poorly can add meatballs to your spaghetti." - _Andrew Hunt_

---
**Modules**

---
Simply put, a module is a Python file containing classes, methods, or functions we would like to use in our code.

Why is that useful?

If we have written projects using classes or functions which handle a particular thing such as displaying graphs, performing calculations, getting user input etc, we can save that code in its own Python file and keep it in our own code library for use in the future. Think back to [article six](https://dev.to/simonchalder/part-six-functions-2bm8) where we made some basic calculator functions. They looked like this:
```
def add(num1, num2):
    print(num1 + num2)

def sub(num1, num2):
    print(num1 - num2)

def div(num1, num2):
    print(num1 / num2)

def mul(num1, num2):
    print(num1 * num2)
```
Now, perhaps we think we may write another project in the future which requires a calculator, instead of writing out all of our functions again we can save this code in its own module.

We save this code in a new file as 'calc.py' and place it in the same folder as our new project file. We can name our modules anything we like as long as we remember to give it the '.py' extension at the end.

We can now start our new project as normal. To import the module we first place the module file in the same folder as our new project file. 
```
├── project_folder
│   ├── new_project.py
│   ├── calc.py
```

Next, we write the following at the beginning of the new project file:
```
import calc
```
Now to access our functions in our new project we simply use the `[modulename].[functionname]`. For example:
```
import calc

print(calc.add(14, 28))

# Output
42
```
Note that for example, if we only needed access to the division method for our project we can import only what we need:
```
from calc import div
```
However, when we call a function this way, we omit the name of the module:
```
add(14, 28)

# Output
42
```

---
**Classes in Modules**

---
Let's now place our calculator in its own class:
```
class calculator:
    def add(num1, num2):
        return num1 + num2

    def sub(num1, num2):
        return num1 - num2

    def div(num1, num2):
        return num1 / num2

    def mul(num1, num2):
        return num1 * num2
```
To access the methods in our class, we need to include the class name in our method call so the syntax would be `[modulename].[classname].[methodname]`:
```
import calc

print(calc.calculator.sub(29, 6))

# Output
3
```

---
**Module Aliases**

---
Some module names can be quite long or complicated. To make things easier to read we can import a module under a different name or alias. Say we had named our calculator module 'calculator.py', we can give it an alias when importing it to make it easier to work with:
```
import calculator as cal
```
Now whenever we need to refer to the calculator module, we simply use 'cal' instead:
```
print(cal.add(12,8)

# Output
20
```

---
**Module Attributes**

---
As well as classes, methods and functions, modules can also be used to store variables and data structures:
```
# calc.py

num1 = 10
num2 = 18
```
To access these variables we simply use `[modulename].[variable]`:
```
# project.py

result = calc.num1 + calc.num2
print(result)

# Output
28
```
Lists, tuples and dictionaries can be accessed in the same manner:
```
# calc.py

my_list = [12, 33, 109, 45]
```
```
# project.py

print(calc.my_list[2])

# Output
109
```

---
**Viewing the contents of a module**

---
Python has a built in function called `dir()` which allows us to view the contents of a module or object. This can be useful when we are unsure if a particular object or module contains the data we require. The `dir()` function will return the contents as a list and so to see it we must either print to the screen or use the returned data in a function.

Let's use our calc module to test this. We can add a couple of variables to our 'calc.py' file as well:
```
num1 = 10
num2 = 18

def add(num1, num2):
        return num1 + num2

def sub(num1, num2):
    return num1 - num2

def div(num1, num2):
    return num1 / num2

def mul(num1, num2):
    return num1 * num2
```
We now print the returned results of our `dir()` function:
```
print(dir(calc))
```
We get the following output:
```
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'add', 'div', 'mul', 'num1', 'num2', 'sub']
```
What we have is a list of everything available to our module. Ignore everything with '__' either side and we can see the names of our functions as well as our 2 variables there too.

No great surprise right? I mean, we created the module so we know what's in it. However, where this becomes useful is when we deal with modules we did not create. This can be Python's built in modules and those written by other people.

---
**Python's Built In Modules**

---


> "One man’s crappy software is another man’s full time job." - _Jessica Gaston_

---

Python ships with all manner of built in, handy modules. Here is [a list](https://docs.python.org/3/py-modindex.html) if you would like to have a read through some of them.

We will cover a couple of them here so you can see how to use them but I encourage you to read around and try out as many as you can. 

To begin let's look at the `math` module (boooooo maths!). Now, maybe your saying to yourself 'but I hate maths, what would I want to use that for?'. Well, the answer is quite simply that it does all of the hard work for you so you don't need to be a maths genius to work with numbers. 

Firstly, go to the page linked above and scroll down to the 'math' module and click the link. What you will see is a list of all of the methods which are part of the 'math' module.

Let's import the math module to get started:
```
import math
```
Simple enough. Scrolling down the methods page, I can see the `pow()` method. This method accepts 2 arguments, the first is our number, and the second is the power we wish to raise our number to. To call the method we simply use `[modulename].[methodname]`. So for example 3 to the power of 3 (3x3x3) is:
```
result = math.pow(3, 3)
print(result)

# Output
27
```
Next let's try the square root method - `sqrt()`. Looking at the documentation, this method accepts a single argument and returns the square root of that argument:
```
result = math.sqrt(64)
print(result)

# Output
8
```
Other methods of note in the `math` module are conversions for degree to radians and visa versa, trigonometric methods for calculating angles such as tree heights, and a method for returning pi for circumference and other circular calculations. 

We can see the other methods which form the math module by using our `dir()` built in function:
```
print(dir(math))
```
We get the following list as output:
```
['__doc__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'cbrt', 'ceil', 'comb', 'copysign', 'cos', 'cosh', 'degrees', 'dist', 'e', 'erf', 'erfc', 'exp', 'exp2', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'isqrt', 'lcm', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 
'nextafter', 'perm', 'pi', 'pow', 'prod', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc', 'ulp']
```
Looking through the list we can see the 'pow' and 'sqrt' methods we used as well as all of the others.

---

For our second example, we will take a look at the `random` module. You may recall way back in my [loops article](https://dev.to/simonchalder/part-five-loops-4jpj) that for the challenge, I generated some random numbers. Well, here is how we can do that.

Again we begin by importing the random module:
```
import random
```
If we look through the methods available in the random module we will see `randrange()`. This method returns a random number between a set range. It accepts 2 arguments, the first is the lowest possible random number, the second is the highest. So for example if we want a random number between 1 and 100 we could do the following:
```
result = random.randrange(1, 100)
print(result)

# Output
23
```
Finally, let's take a look at the random choice method - `choice()`. This method accepts a single argument which can be a list or tuple. The method will then randomly select one of the list items and return it:
```
my_list = [1,22,57,86,12]

result = random.choice(my_list)
print(result)

# Output
57
```
Again let's take a look at this module with our `dir()` function:
```
print(dir(random))
```
We get this list as an output:
```
['BPF', 'LOG4', 'NV_MAGICCONST', 'RECIP_BPF', 'Random', 'SG_MAGICCONST', 'SystemRandom', 'TWOPI', '_ONE', '_Sequence', '_Set', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_accumulate', '_acos', '_bisect', '_ceil', '_cos', '_e', '_exp', '_floor', '_index', '_inst', '_isfinite', '_log', '_os', '_pi', '_random', '_repeat', '_sha512', '_sin', '_sqrt', '_test', '_test_generator', '_urandom', '_warn', 'betavariate', 'choice', 'choices', 'expovariate', 'gammavariate', 'gauss', 'getrandbits', 'getstate', 'lognormvariate', 'normalvariate', 'paretovariate', 'randbytes', 'randint', 'random', 'randrange', 
'sample', 'seed', 'setstate', 'shuffle', 'triangular', 'uniform', 'vonmisesvariate', 'weibullvariate']
```

---
**Conclusion**

---

Hopefully this has started to demonstrate that many common things we may need for our projects are already contained in Python's built in modules which saves us the time of having to code this ourselves every time. By using our own modules, we can re-use our code in future projects to save us some time there too. In addition, we can now start to break up our code into separate files with each module having a particular purpose. We then simply import those files into our main project file to use their code. This has the advantage of making the code modular so that we know exactly where to look to work on a specific part of the application even if our project becomes very large overall.

In the next and final article, I want to demonstrate how you can import other people's modules into your code from the internet as well as taking a look at some modules which are likely to be of use to the land based sector.

As always, thank you for reading and I look forward to seeing you next time.

Simon.