- Modules are just objects of type
ModuleType
. They act like a dictionary that holds references to objects it holds; module.__dict__
.
- We can set/delete attributes.
module.x = 10
is the same as module.__dict__['x'] = 10
- When importing a module, it executes the module from top to bottom before returning to the caller.
- Module can be namespace, py file, execution environment for statements or container of global variables.
- The dictionary has preset attributes such as
__path__
, __loader__
, etc. Main attributes:
__name__
: Module name
__file__
: Associated source file (if any)
__doc__
: Docstring
__path__
: Package path. It is used to look for package subcomponents
__package__
: The module’s __package__
attribute must be set. Its value must be a string, but it can be the same value as its __name__
. When the module is a package, its __package__
value should be set to its __name__
. When the module is not a package, __package__
should be set to the empty string for top-level modules, or for submodules, to the parent package’s name.
__spec__
: Module spec
- The module’s attributes are set after creation and before execution
- The main difference between modules and packages is that packages have
__path__
and __package__
defined (not None
)
sys.modules
serves as a cache for all imported modules/packages
- It is a dictionary so we can delete/set keys
- If we delete a module, it will force Python to import it when we reimport it
- If we set module key to
None
-> result in ModuleNotFoundError
- Even if we import one object from a module/package, the module/package will be cached in the sys.modules but not available in the global name space
- The module created during loading and passed to
exec_module()
may not be the one returned at the end of the import
- This can happen if the imported module set the
sys.modules[__name__]
to some other module
- Execution of the module is what populates the module’s
__dict__
(namespace of the module). This is done by the loader
- When a submodule is loaded using any mechanism, a binding is placed in the parent module’s namespace to the submodule object. For example, if we have a package called spam that has a submodule foo and it imports any of its objects like
from .foo import x
, after importing spam, spam will have an attribute foo which is bound to the submodule -> We can now use spam.foo
- Relative imports use leading dots. A single leading dot indicates a relative import, starting with the current package. Two or more leading dots indicate a relative import to the parent(s) of the current package, one level per dot after the first.
- Can only use this form of import:
from <> import <>
- It can’t use
import .<>
because this is not a valid expression
- Absolute imports have to start from the top level package and go downward to refer to the module:
from package.subpackage import module
- Not recommended because if we change the name of the package then we need to change all the import statements -> relative imports are more robust and don’t care about namings