Python Journal - The Curious Case of Python Imports
Table of Contents
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__)
No comments:
Post a Comment