The Python Language Reference, Release 3.9.5 5.3 Searching To begin the search, Python needs the fully qualified name of the module (or package, but for the purposes of this dis- cussion, the difference is immaterial) being imported. This name may come from various arguments to the import statement, or from the parameters to the$_{importlib.import_module()}$ or$_{__import__()}$ functions. This name will be used in various phases of the import search, and it may be the dotted path to a submodule, e.g. foo.bar.baz . In this case, Python first tries to import foo , then foo.bar , and finally foo.bar.baz . If any of the intermediate imports fail, a$_{ModuleNotFoundError}$ is raised. The first place checked during import search is sys.modules . This mapping serves as a cache of all modules that have been previously imported, including the intermediate paths. So if foo.bar.baz was previously imported, sys.modules will contain entries for$_{foo}$,$_{foo.bar}$ , and$_{foo.bar.baz}$ . Each key will have as its value the corresponding module object. During import, the module name is looked up in$_{sys.modules}$ and if present, the associated value is the module satisfying the import, and the process completes. However, if the value is$_{None}$ , then a$_{ModuleNotFoundError}$ is raised. If the module name is missing, Python will continue searching for the module. sys.modules is writable. Deleting a key may not destroy the associated module (as other modules may hold references to it), but it will invalidate the cache entry for the named module, causing Python to search anew for the named module upon its next import. The key can also be assigned to$_{None}$ , forcing the next import of the module to result in a$_{ModuleNotFoundError}$ . Beware though, as if you keep a reference to the module object, invalidate its cache entry in$_{sys.modules}$ , and then re-import the named module, the two module objects will not be the same. By contrast,$_{importlib.reload()}$ will reuse the same module object, and simply reinitialise the module contents by rerunning the module’s code. 5.3.1 The module cache 5.3.2 Finders and loaders If the named module is not found in$_{sys.modules}$ , then Python’s import protocol is invoked to find and load the module. This protocol consists of two conceptual objects, finders and loaders . A finder’s job is to determine whether it can find the named module using whatever strategy it knows about. Objects that implement both of these interfaces are referred to as importers - they return themselves when they find that they can load the requested module. Python includes a number of default finders and importers. The first one knows how to locate built-in modules, and the second knows how to locate frozen modules. A third default finder searches an import path for modules. The import path is a list of locations that may name file system paths or zip files. It can also be extended to search for any locatable resource, such as those identified by URLs. The import machinery is extensible, so new finders can be added to extend the range and scope of module searching. Finders do not actually load modules. If they can find the named module, they return a module spec , an encapsulation of the module’s import-related information, which the import machinery then uses when loading the module. The following sections describe the protocol for finders and loaders in more detail, including how you can create and register new ones to extend the import machinery. Changed in version 3.4: In previous versions of Python, finders returned loaders directly, whereas now they return module specs which contain loaders. Loaders are still used during import but have fewer responsibilities. 51 5.3. Searching