Skip to content

Instantly share code, notes, and snippets.

@cameronwlewis
Forked from twolfson/README.md
Created December 20, 2018 08:12
Show Gist options
  • Save cameronwlewis/5382abb30b4d6309820d3a15f7be909b to your computer and use it in GitHub Desktop.
Save cameronwlewis/5382abb30b4d6309820d3a15f7be909b to your computer and use it in GitHub Desktop.
How Python modules are resolved

Python modules have 2 syntaxes for importing.

import x
from x import y

import x

Absolute imports are used for importing top-level modules and only top level modules. These are ones that are globally installed, either inside of Python core or via a package manager (e.g. pip).

import json  # Python core
import yaml  # Installed via `pip install pyyaml`

When you declare a global import, Python looks for the __init__.py of a top-level module. When you are working inside of a virtual environment, this is

~/.virtualenvs/<your_virtualenv>/lib/python2.7/site-packages/<package_name>/__init__.py

If this file is populated, it will return all variables declared at the top scope.

# hello/__init__.py

# This file returns `a` and `c` when imported
a = 'b'
def c():
  d = 'e'

Python 2 note

THIS SECTION SHOULD BE AVOIDED IF YOU ARE TRYING TO DEVELOP CROSS-VERSION MODULES

Inside Python 2, import x can resolve local files as modules. There are 2 cases:

world/__init__.py

If a folder is named the same as the top level module, an __init__.py is looked for. If that exists, then we will follow the same variable procedure as an absolute module (if empty, collect from sibling files. otherwise, return exposed variables.)

hello/
    hello.py # `import world`
    world/
        __init__.py # first looks for this

# If not found, looks here
~/.virtualenvs/<your_virtualenv>/lib/python2.7/site-packages/world/__init__.py

world.py

If a file is named the same as the top level module, the local file's contents will be returned.

hello/
    hello.py # `import world`
    world.py # first looks for this

# If not found, looks here
~/.virtualenvs/<your_virtualenv>/lib/python2.7/site-packages/world/__init__.py

Order

The order that these cases are dealt with is folder -> file -> top-level

hello/
    hello.py # `import world`
    world/
        __init__.py # first looks for this
    world.py # second looks here

# If not found, looks here
~/.virtualenvs/<your_virtualenv>/lib/python2.7/site-packages/world/__init__.py

from x import y

Requests for absolute modules are returned the exact same between from x import y and import x. The difference is that the items returned as only those in the import y piece.

# hello/__init__.py

# `from hello import a` will return `a` as a variable
a = 'b'
def c():
  d = 'e'

In addition to this, Python 2.5+ and 3 support relative imports.

# Inside of hello/hello.py
from .world import status

When resolving a relative module, Python first looks for a folder with that name and its __init__.py. If that exists, it will extract the variable from the __init__.py as in import x. If it does not exist, it will look for world.py at the same level as the import. If that is not found, an Exception will be raised.

hello/
    hello.py  # Resolving .world from hello/hello.py
    world/
      __init__.py  # Looks for file here
    world.py  # Looks here second
# If not found, raise an exception

Once resolved, the requested variables inside of the module are returned. If these don't exist an exception will be raised.

# hello/world.py
status = 'Operational'
time = 'Now'

# Inside of hello/hello.py
from .world import status  # 'Operational'

Sticking point

This is something that kept on catching me off-guard.

YOU CANNOT IMPORT A RELATIVE MODULE IN ITS ENTIRETY IN PYTHON 3.

YOU CAN ONLY IMPORT EXPLICIT VARIBLES!

import world  # world.a, world.c are accessible
# No way to do this in Python 3.
# Must be explicit:
from .world import status, time
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment