Search code examples
pythonsys

Why do i need to explicitly import modules when they're already imported?


Whenever I run the command on Ubuntu Linux:

python3 -v

I get a verbose output which tells me:

import _frozen_importlib # frozen
import _imp # builtin
import sys # builtin
import '_warnings' # <class '_frozen_importlib.BuiltinImporter'>
import '_thread' # <class '_frozen_importlib.BuiltinImporter'>
import '_weakref' # <class '_frozen_importlib.BuiltinImporter'>
import '_frozen_importlib_external' # <class '_frozen_importlib.FrozenImporter'>
import '_io' # <class '_frozen_importlib.BuiltinImporter'>

And much much more.

However, I see that import sys was executed, yet I still need to manually import sys to use it. Why is this happening?


Solution

  • The short (and inaccurate) version: import foo does two separate things:

    • it looks for and loads from disc the module named "foo", if necessary
    • it binds the module to the name foo in the current namespace

    You have to "manually import" sys for the same reason that this doesn't work:

    >>> def r():
    ...     import random
    ...     print(random.randint(0, 100))
    ... 
    >>> r()
    26
    >>> random.randint(10, 20)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'random' is not defined
    

    A way to demonstrate that import is nothing magic:

    $ cat foo.py 
    print('foo has been imported')
    
    def fn():
        print('fn has been called')
    $ python
    >>> import importlib
    >>> bar = importlib.__import__('foo')
    foo has been imported
    >>> bar.fn()
    fn has been called
    

    The module name works like any other name in Python:

    importlib.__import__() creates a value (a module, in this case) and the = assigns it to the name.

    With a conventional import, import foo creates a value (the same module) and assigns it to the given name (which just happens to be the same as the module name).

    You can verify that a module is only loaded once like this:

    $ python
    >>> import foo
    foo has been imported
    >>> import foo
    >>>
    

    and you can demonstrate that a module is just another value:

    >>> import foo
    foo has been imported
    >>> foo.fn()
    fn has been called
    >>> bar = foo
    >>> bar.fn()
    fn has been called