Let’s actually code the example we’ve been talking about to show how initialization files and paths come into play. The following three files are coded in a directory dir1 and its subdirectory dir2—comments give the pathnames of these files:
# dir1\__init__.py print('dir1 init') x = 1 # dir1\dir2\__init__.py print('dir2 init') y = 2 # dir1\dir2\mod.py print('in mod.py') z = 3
Here, dir1 will be either an immediate subdirectory of the one we’re working in (i.e., the home directory), or an immediate subdirectory of a directory that is listed on the module search path (technically, on sys.path). Either way, dir1’s container does not need an __init__.py file.
import statements run each directory’s initialization file the first time that directory is traversed, as Python descends the path; print statements are included here to trace their execution:
C:\code>python# Run in dir1's container directory >>>import dir1.dir2.mod# First imports run init files dir1 init dir2 init in mod.py >>> >>>import dir1.dir2.mod# Later imports do not
Just like module files, an already imported directory may be passed to reload to force reexecution of that single item. As shown here, reload accepts a dotted pathname to reload nested directories and files:
>>>from imp import reload# from needed in 3.X only >>>reload(dir1)dir1 init <module 'dir1' from '.\\dir1\\__init__.py'> >>> >>>reload(dir1.dir2)dir2 init <module 'dir1.dir2' from '.\\dir1\\dir2\\__init__.py'>
Once imported, the path in your import statement becomes a nested object path in your script. Here, mod is an object nested in the object dir2, which in turn is nested in the object dir1:
>>>dir1<module 'dir1' from '.\\dir1\\__init__.py'> >>>dir1.dir2<module 'dir1.dir2' from '.\\dir1\\dir2\\__init__.py'> >>>dir1.dir2.mod<module 'dir1.dir2.mod' from '.\\dir1\\dir2\\mod.py'>
In fact, each directory name in the path becomes a variable assigned to a module object whose namespace is initialized by all the assignments in that directory’s __init__.py file. dir1.x refers to the variable x assigned in dir1\__init__.py, much as mod.z refers to the variable z assigned in mod.py:
>>>dir1.x1 >>>dir1.dir2.y2 >>>dir1.dir2.mod.z3
import statements can be somewhat inconvenient to use with packages, because you may have to retype the paths frequently in your program. In the prior section’s example, for instance, you must retype and rerun the full path from dir1 each time you want to reach z. If you try to access dir2 or mod directly, you’ll get an error:
>>>dir2.modNameError: name 'dir2' is not defined >>>mod.zNameError: name 'mod' is not defined
It’s often more convenient, therefore, to use the from statement with packages to avoid retyping the paths at each access. Perhaps more importantly, if you ever restructure your directory tree, the from statement requires just one path update in your code, whereas imports may require many. The import as extension, discussed formally in the next chapter, can also help here by providing a shorter synonym for the full path, and a renaming tool when the same name appears in multiple modules:
C:\code>python>>>from dir1.dir2 import mod# Code path here only dir1 init dir2 init in mod.py >>>mod.z# Don't repeat path 3 >>>from dir1.dir2.mod import z>>>z3 >>>import dir1.dir2.mod as mod# Use shorter name (see Chapter 25) >>>mod.z3 >>>from dir1.dir2.mod import z as modz# Ditto if names clash (see Chapter 25) >>>modz3