In this offering of PIC16A, we have primarily done our coding in the Jupyter Notebook interface, which emphasizes interactivity and exploratory coding. Another useful skill is writing and using your own modules (i.e. .py
files), which can hold useful classes and functions that you would like to reuse. In this lecture, we'll go into some more detail on how to create and use modules.
At core, the import
keyword is just a fancy way to run the code in one or more .py
files -- the main difference is that the import
keyword will additionally assign objects and functions created within those files to the module's namespace. That is, a function f
defined in module m
will have name m.f
. We've seen ways to use from
and as
to further manipulate how names are organized.
Because import
literally runs code, we can also use it to execute other commands in local .py
files. Here's an example:
# hello.py :
# print("Hi there!")
# name = input("What's your name?")
# print("Nice to meet you, " + name + "!")
import hello
# ---
Hi there! What's your name?Jean-Luc Picard Nice to meet you, Jean-Luc Picard!
Because the import
keyword checks whether a module has already been loaded, you can only import
a given file once per session (In Jupyter, you can "restart the kernel" to start a new session). So, running import hello
again doesn't do anything.
import hello
While it is possible to run the same external code multiple times, this is almost always bad practice and we will not discuss it in this course. Instead, structure your code so that each file needs to be imported only once.
When you have many hundreds, thousands, or tens of thousands of lines of code, you don't want to put them all in the same .py
file. Instead, it's common to split these up into multiple files. A package is a directory structure containing multiple modules, alongside a special __init__.py
file that tells the Python interpreter that the files in the given directory should be treated as modules. These directories can be arbitrarily nested.
Here's an example, with the following directory structure:
example_module/
+-- __init__.py
+-- top_level.py
+-- example_submodule/
+-- __init__.py
+-- funs_1.py
+-- funs_2.py
Both of the files __init__.py
are completely empty -- the only thing that matters is their name. Once these files are in place, we can use import
in exactly the way we did previously on modules written by others.
from example_module import top_level
top_level.describe()
# ---
function imported from example_module.top_level
# importing submodules
from example_module.example_submodule import funs_1, funs_2
funs_1.describe_1()
# ---
function imported from example_module.example_submodule.funs_1
funs_2.describe_2()
# ---
function imported from example_module.example_submodule.funs_2
A very common pattern is to write a single .py
file that contains both function or object definitions and imperative commands (e.g. function calls). For example:
# file: boldly.py
# function definitions
def boldly_print():
print("to boldly go")
# imperative commands
boldly_print()
We might decide that boldly_print()
is a super-good function, and that we'd like to use it in other projects without rewriting the code. However, we don't always want to call boldly_print()
immediately, as would happen if we ran import boldly
.
For this reason, Python defines a special __name__
variable. In the global scope, __name__
has value "__main__"
.
__name__
'__main__'
On the other hand, when a module is import
ed, the value of __name__
is the name of the module. This means we can check whether the module is being treated as a script (i.e. run in global scope) or imported (so that we can use its definitions without running its imperative commands). Here's an example:
# file: boldly2.py
def boldly_print():
print("to boldly go")
print(__name__)
if __name__ == "__main__":
boldly_print()
import boldly2 # prints the value of __name__
# ---
boldly2
In this course, we don't frequently run modules as scripts, but it's a good thing to know how to do. If you like the command line, then you can just do this after navigating to the appropriate directory:
> python3 boldly2.py
__main__
to boldly go
A more comfortable approach for many is to use the Spyder IDE.