python: modules and import conventions

This commit is contained in:
thomasabishop 2023-08-18 19:34:17 +01:00
parent d0056360ad
commit 933f1baa4a
2 changed files with 170 additions and 137 deletions

View file

@ -1,137 +0,0 @@
---
categories:
- Programming Languages
tags: [python, OOP]
---
# Python modules
Modules in Python are very similar to modules in Node.js: a combination of several pieces of functionality (often written in a mix of different syntactic styles) bundled together to be used as a unit.
## Creating modules
### Naming modules
The name of a module is the name of its file, e.g. `utils.py` defines the module `utils`. When we import a module we do not need to specify the extension.
### Example module
Here is an example of a module. We will use this throughout.
```py
"""utils module"""
print('Hello I am the utils module')
def printer(some_object):
print('printer')
print(some_object)
print('done')
class Shape:
def __init__(self, id):
self.id = id
def __str__(self):
return 'Shape - ' + self.id
default_shape = Shape('square')
def _special_function():
print('Special function')
```
## Importing modules
> Whether we are importing our own local module, a third-party module or a built-in module native to Python, the syntax is the same.
To use the module defined in `utils.py`:
### Method one
```py
import utils
```
Now we can use the functionality provided by the module, e.g:
```py
import utils
utils.printer(utils.default_shape)
shape = utils.Shape("circle")
```
Here we have invoked the `printer()` function contained in the module. We have used it to print the `default_shape` variable in `utils` which is itself and instance of the `Shape` class, also defined in `utils`.
After this we have created a new instance of the `Shape` class in the file that imports `utils`.
### Method two
In the last example we used the name `utils` to reference our import. I prefer this because it shows the origin of the code you are using. But if we imported like so:
```py
from utils import *
```
Then we wouldn't need to prepend our use of `utils` functions with `utils`. Instead out code would look like this:
```py
from utils import *
printer(default_shape)
shape = Shape("circle")
```
### Importing a subset of the module
It doesn't look like you need to explicity export the values you want to be able to use once your module has been imported in Python. Everything seems to be exported and accessible.
However, you can choose to import only a subset of the module:
```py
from utils import Shape
```
And you can use an alias, e.g:
```py
from util import Shape as banana
```
## Main module as program entry point
If we have any freestanding code in a module. It will execute the moment the file it is imported into executes. Examples of this in the module example are the print function at the top and the instantiation of the `shape` object.
Typically you will only want this to happen when **the module is the entrypoint** to the program. In Python the entrypoint is known as the `main` module. This is analagous to `index.js` in a Node program.
When the module is the entry point it has a special name: `__main__`. We can test for this condition and only run code if the module is being run as the main module. This is a common pattern:
```py
if __name__ == "__main__":
print("This runs only if the module is the main module")
```
`__name__` is a reference to the current module name.
> We do not need to name our module `main` for this to occur. Whenever an imported module is run it occupies the position of `__main__`
To ensure that certain code runs on initialisation we can define a special `main` function or class method that runs when the module is loaded. This is effectively the set-up code in the entry point, e.g:
```py
def main():
printer(default_shape)
shape = Shape("circle")
printer(shape)
if __name__ == "__main__":
main()
```

View file

@ -0,0 +1,170 @@
---
categories:
- Programming Languages
tags: [python, OOP]
---
# Python modules
## Distinguishing _modules_ from _packages_
Modules and packages in Python are very similar to modules in Node.js: a combination of several pieces of functionality (often written in a mix of different syntactic styles) bundled together to be used as a unit.
- A **module** is basically just a script - a single Python file that you can import using the `import` statement
- A **package** is a _collection of modules_
A package is a whole directory which has an `__init__.py` at its root which designates it as a package (this can be empty, but it is necessary to have this file for the interpreter to understand that the directory is a package).
## Creating modules
### Naming modules
The name of a module is the name of its file, e.g. `utils.py` defines the module `utils`. When we import a module we do not need to specify the extension.
### Example module
Here is an example of a module.
```py
"""utils module"""
print('Hello I am the utils module')
def printer(some_object):
print('printer')
print(some_object)
print('done')
class Shape:
def __init__(self, id):
self.id = id
def __str__(self):
return 'Shape - ' + self.id
default_shape = Shape('square')
def _special_function():
print('Special function')
```
## Importing modules
> Whether we are importing our own local module, a third-party module or a built-in module native to Python, the syntax is the same.
- If the module is in the same directory as the file you wish to import it into, you can just directly import with:
```py
import utils
utils.specialFunction
shape = utils.Shape('circle')
```
- If the module you want to import is in a subdirectory, you will need to make that subdirectory a package in order to be able to import from it. This means adding an `__init__.py`. Then you can use the following import syntax:
```py
from subdir import utils
...
```
### Different import syntax
- If you don't want to have to prepend the imported module's name when using it (e.g. `utils.specialFunction`). You can import like so:
```py
from utils import *
specialFunction()
```
- By default an import makes everything in the module available, however you can choose to import only a subset. E.g.:
```py
from utils import Shape
# or with an alias:
from utils import Shape as lorem
```
## `__main__`
The `__main__` attribute is important in Python.
### Usage in scripts/modules
When you run a script (module) Python assigns the string `__main__` to the `__name__` attribute to the script that is being executed.
If you run the script as an import into another script, the `__name__` attribute of the imported module is set to the module name, not `__main__`. This is why, when we import we say `import [module_name]` and not `import __main__`.
When used with a module, `__main__` indicates code that should be run straight away when the given script file is run. You won't always want to execute everything in the module file on execution so you can put this stuff outside of `__main__`
Typically you will only want immediate execution to happen when **the module is the entrypoint** to the program.
We indicate this with `__main__` as follows:
```py
if __name__ == "__main__":
print("This runs only if the module is the main module")
```
### Usage in packages
The only real difference with packages is that you can also define a `__main__.py` file in the root of the package directory.
This functions in the same manner as `__main__` in an individual module however this time it is not the entry point for a single script but the entrypoint of the package as a whole.
> When the package is run directly as a script, the code in `__main__.py` is executed, just like it would be if it were a standalone script. This allows a package to be run as a script, as well as letting it contain modules that can be imported separately.
You can of course still have individual entrypoints for the modules that comprise the package.
### Running the package as a package
If you want to run the whole package at once, rather than individually importing the modules that belong to it. You can run the following from the command-line:
```
python -m package_name
```
With this, Python will try to locate the `__main__.py` file and execute it as though it were a standalone script.
### Using a `main()` function
To ensure that certain code runs on initialisation we can define a special `main` function or class method that runs when the module or package is loaded.We would put the main set-up logic in `main` and coordinate the execution of the module or package through it.
```py
def main():
printer(default_shape)
shape = Shape("circle")
printer(shape)
if __name__ == "__main__":
main()
```
### Example
```py
'''palindrome module'''
def is_palindrome(inp):
inp_list = list(inp)
inp_list_len = len(inp_list)
for i in range(inp_list_len):
if inp_list[i] != inp_list[inp_list_len - 1 - i]:
return False
return True
def main():
palindrome_one = "soros"
palindrome_two = "torot"
not_palindrome = "chair"
print(is_palindrome(palindrome_one))
print(is_palindrome(palindrome_two))
print(is_palindrome(not_palindrome))
if __name__ == '__main__':
main()
```