Wednesday, 15 May 2019

Python Journal - The curious case of Python imports

Python Journal - The Curious Case of Python Imports

Python Journal - The Curious Case of Python Imports

1 The import statement

Python has a module system for easier maintenance of large codebases. This enables us to put Python statements and definitions in different files and 'import' them whenever needed in another script. Consider following script names 'constants.py'

constants.py

PI = 3.14
EXP = 2.7182

Our script 'constants.py' contains constants PI, and EXP (natural logarithmic base). If we ever need to use these constants, we can just import them from 'constants.py'

constants_tester.py

import constants

def get_area(circle_radius):
    return constants.PI * (circle_radius ** 2)

Another way to import constants is

from constants import PI

def get_area(circle_radius):
    return PI * (circle_radius ** 2)

This example only imports PI from our constants.py module. However, there's one question that I asked myself, if Python runs the module script (constants.py script) while importing it in another script, does the statement 'from constants import PI' also imports EXP?

2 Selective imports

from constants import PI

def get_area(circle_radius):
    return constants.PI * (circle_radius ** 2)

print(EXP)

We get following error

Traceback (most recent call last):
  File "constants_tester.py", line 11, in <module>
    print(EXP)
NameError: name 'EXP' is not defined

Which means it does not import EXP. Let's see if we can import EXP using constants module name

from constants import PI


def get_area(circle_radius):
    return PI * (circle_radius ** 2)

print(constants.EXP)

This also gives error as following

Traceback (most recent call last):
  File "constants_tester.py", line 10, in <module>
    print(constants.EXP)
NameError: name 'constants' is not defined

Interesting! This means that when we say 'from constants import PI', it just imports PI in the current namespace. It doesn't even recognize 'constants'

But what exactly happens under the hood?

3 Investigating the imports

We could look into globals(), locals(), and sys.modules to see what exactly Python imports

from constants import PI
import sys
from pprint import pprint

def get_area(circle_radius):
    return PI * (circle_radius ** 2)


pprint(globals()['PI'])
pprint(locals()['PI'])
pprint(sys.modules['constants'])

This gives following output

3.141592653589793
3.141592653589793
<module 'constants' from '/home/chinmaybhoir/PycharmProjects/python-practice/constants.py'>

You can see that sys.modules has an entry for 'constants', so the module is loaded. Why can not we access it then?

4 The __import__ function

Python has a built-in function __import__ that is called whenever the statement import is executed.

Following is taken verbatim from __import__ documentation

For example, the statement import spam results in bytecode resembling the following code:

spam = __import__('spam', globals(), locals(), [], 0)

The statement import spam.ham results in this call:

spam = __import__('spam.ham', globals(), locals(), [], 0)

Note how __import__ returns the toplevel module here because this is the object that is bound to a name by the import statement.

On the other hand, the statement from spam.ham import eggs, sausage as saus results in

_temp = __import__('spam.ham', globals(), locals(), ['eggs', 'sausage'], 0)
eggs = _temp.eggs
saus = _temp.sausage

You can see that the last usecase imports the module in a temporary variable _temp, and the variables (PI/EXP in our usecase) are assigned differently. This is the reason we can not access 'constants', or 'EXP' (since it does not appear in the fromlist attribute of __import__)

Author: Chinmay.Bhoir

Created: 2019-05-17 Fri 17:20

Emacs 24.5.1 (Org mode 8.2.10)

Validate

No comments:

Post a Comment

Python Journal - The curious case of Python imports

Python Journal - The Curious Case of Python Imports Python Journal - The Curious Case of Python Imports Table of Co...