What are Python Modules?
Tags: python, beginners
Published: 2020-05-23 20:36:46

<h4>What is modular programming?</h4>
Modular programming is a design principle that allows **complex unit** to be broken into **smaller, more manageable pieces**. Breaking down complex system into smaller units has several benefits: 

* **Effective reusability** - The same function when needed in several parts of the application could be effectively reused.     
* **Code readability** -  Typical medium to large size project could have thousands of lines of code. Without modularity, it is very hard to read and maintain the application.
* **Division of responsibility** - Applications with modules could be effectively developed independently by more than one developer.    
* **Effective debugging** - It is easier to spot and fix bugs when the codebase is broken into modules. 

<h4>How to create modules in Python?</h4>
Writing modules in Python is straight forward. Every file written in Python with a **.py** extension is considered a module. So, there is no special keyword to create modules in Python. 
 
Here is a simple python module called **hello.py** containing two functions that prints hello or goodbye several times depending on the argument passed:
```python
def say_hello(times):
    print(“Hello!\n”*times))

def say_bye(times):
    print(“Goodbye!\n”*times))
```

<h4>How to reuse modules?</h4>
Functions defined in the `hello` module should be first imported before it can be called from another part of the application. 
```python
import hello

#Say hello 10 times 
hello.say_hello(10)
```
Note that import statements can be placed anywhere in the Python file. But it is a good practice to place all of the imports on top of the file.

<h4>What is a direct import?</h4>
Modules could also be imported directly using below syntax. With this direct import approach, names get loaded into the calling module’s local symbol table and they don’t have to be called with `<module>.` syntax. 
```python
from math import pow, pi  

radius = 5
area = pi * pow(radius, 2)  # 78.53981633974483
```

<h4>How to import all names of a module?</h4>
All names defined in a module could be imported directly using `from <module> import *` statement. With this import, all names except the ones defined with an underscore “_” are imported. This is **not** a recommended approach. Because if two modules happen to have functions with the same name,  it is harder to understand which module’s function is actually used by the calling module.    

<h4>What happens if there are duplicate names?</h4>
If there are duplicate names in imported modules, a duplicate name defined later will replace the former. Numpy module’s `sin` function will be used by the below module: 
```python
from math import sin
from numpy import sin 
```

<h4>How to provide alias names during import?</h4>
Imported modules or direct import names could be renamed for code readability. 
```python
import numpy as np
from math import pow as power

print(np.sin(10))

print(power(10, 2))
```

<h4>How does a Python interpreter find modules?</h4>
If a module named `hello` is imported using `import hello` statement, the Python interpreter searches for this module in the following locations and in the order given:
* The directory of the top-level file, i.e. the file being executed.
* The directories of PYTHONPATH, if this global environment variable is set.
* Standard Python installation path 

Care must be taken to not to name the custom module same as standard modules. As local modules are given higher precedence, applications could end up replacing standard module’s behavior with local module. 

<h4>How to run module as a script?</h4>
Same Python file could be used as a script or module depending on the presence of system variable `__name__  == "__main__"` condition. so if the below segment is added to the `hello.py` module, `hello` module could be run as a script.   
```python
if __name__ == "__main__":
    import sys
    say_hello(int(sys.argv[1]))

#Runs say_hello when invoked
$ python hello.py 5
``` 

<h4>How to get module information?</h4>

List of built-in modules could be printed using:
```python
import sys
print(sys.builtin_module_names)
```

Module’s content could be printed using `dir` command:
```python
import math
dir(math)
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']

# prints all built-ins
import builtins
dir(builtins)

# prints all names in local scope
dir() 

```