Importing Modules in Python

Memory Concerns

First things first! Let’s get the unnecessary memory concerns out of the way. Run Python interpreter. Type the following snippet and execute it.

import sys

print(sys.modules)

As you know Python adds imported modules to sys.modules dict to keep track of them. Thus, when we import a module, it’s used from sys.modules without a lookup, if it’s previously imported. Let’s execute the following snippet.

print(sys.modules.get('pprint'))

# None

Right, it’s None as expected. Let’s import pprint module, like so;

import pprint

Let’s check sys.modules again.

print(sys.modules.get('pprint'))

# <module 'pprint' from '<PATH>/pprint.py'>

We’ve imported pprint module and it’s in sys.modules as expected. Now launch a new Python interpreter and execute the following snippet.

from pprint import pprint

Now it’s time to check sys.modules again, like so;

import sys
print(sys.modules.get('pprint'))

# <module 'pprint' from '<PATH>/pprint.py'>

The result, without a surprise is the same. When we use from X import Y statement, we simply get a “handle” to the object we “import” and the entire module, which contains the object itself actually gets imported. Just because we use from X import Y statement to “import” an object into local scope doesn’t mean that the entire Python module doesn’t get imported. It’s quite the contrary as we’ve just seen.

In a nutshell, Python doesn’t support importing only an object without importing the entire module, which the object belongs to. Therefore, using from x import y statement compare to import x doesn’t really save memory at all. So using from statement “only” with memory concerns is not a valid argument.

Conflicts and Confusing Hacks

Using from X import Y statement will cause other issues such as conflicts. Say we have two classes with the same name, AssetVersion in different modules/packages. I do not even get into why in the world there would be two classes with the exact same name particularly in a development department where from x import y is furiously promoted to be used instead of plain old import statement, but this is truly a real world scenario. For argument sake, we assume that one of them a model class, which interacts with database.

In a scenario where we need to use both classes with from X import Y statement will present a conflict as expected, like so;

from assetSystem.asset import AssetVersion
from assetDatabase.asset import AssetVersion # BOOM!

Well, here we go! If you’ve been a Python developer for some time, you would know how common this situation may become. But wait! There is a solution for that, isn’t there? Of course there is and it’s…

from assetSystem.asset import AssetVersion
from assetDatabase.asset import AssetVersion as AssetVersionModel

Do I even need to say don’t do this? It’s obvious why we shouldn’t do that but I’ll mention about it in the next section.

Importing from init Module

Importing every available object of a package into it’s __init__.py “may” appear logical thing to do for some people since when it comes to importing objects from another package will arguably be shorter. Say we have the following;

from assetSystem import AssetBundle

Imagine there is no HTML document for assetSystem package, which we would refer and see where AssetBundle object comes from. It might be buried anywhere in the package. So we have to navigate to __init__.py module to see where the object is imported from. This is additionally painful if we need to fix a bug, where we need to dig into plenty of imported references in __init__.py modules in different packages in order to understand what’s going on. Again, this is a real world problem, and it gets worse when you need to clone those dependent packages or search through them in repositories to track the issue.

Give it a thought for a second. Would that help you to understand the architecture of the packages you work with? Wouldn’t it waste your precious time? Please encourage your colleagues to search a job in another field if they say something like “Who cares understanding the architecture of the packages/APIs I use, I only work here (9 to 6)”.

Seeing is Understanding

Needless to say, understanding the libraries, frameworks that we use is important. Their inner mechanics and architecture are also greatly represented by the organization of the packages/modules. A great way to understand them is to use them in our code like so;

import sDocument.doxygen.packagePython
import sDocument.doxygen.packageCPP

Apart from seeing the structure when we see these imports, we can also see the consistency, which obviously makes us more productive in our development work. Imagine sDocument.doxygen package contains a C# related module. What would the name of that module be? Easy to guess, isn’t it?

Take a look at the following snippet, where we import createFromNode function from a package’s __init__.py

from assetSystem import createFromNode

As normal as it looks, this import doesn’t give us a clue in terms of context. createFromNode of what? From a Maya node? From a Nuke node? We may also consider it as a generic function that we can use in any DCC, which I’d highly doubt about. Now consider the following snippet.

import assetSystem.maya.createFromNode
import assetSystem.nuke.createFromNode
import assetSystem.generic.createFromNode

Doesn’t it appear clearer? I can clearly see that createFromNode functions are imported from their respective modules, and of course if we had a generic createFromNode function, it would be imported from its respective module too.

So, seeing the entire import path helps you understanding the architecture, if this is of course something you’d care about.

I wasted many precious hours of tracking what a generically named function like createFromNode would do and where it comes from. I’ve seen other developers wasting their precious hours because of same issue too. I don’t know what you would think but I prefer to spend my precious time to produce best work I can produce instead of tracking/debugging things, which shouldn’t be a problem to begin with.

Documentation

Generating HTML documentation for Python code via Doxygen offers amazing navigation capabilities. The objects we use for our method/function arguments, inherited classes, class members and returns are used by Doxygen to generate navigation automatically. Thus, you can navigate one object/package/module to another in the HTML docs and it’s without a doubt a huge time saver.

For instance, as you can see in the screenshot below mFileSystem.jsonFileLib.JSONFile class inherits mFileSystem.fileLib.File class, which you can click on and navigate to.

Likewise setPreBuild method in the screenshot below accepts two arguments, allLib and envEntryContainer that expect mMeco.libs.allLib.All and mMeco.libs.entryLib.EnvEntryContainer class instances respectively. You can click on them to see their API reference documentation. It’s literally “what you see is what you get”.

What About from x import *

Simply don’t.

Recap

Using import statement is better than using from x import y (as z) for the reasons I’ve stated above.

  • It doesn’t cost more memory
  • No conflicts, therefore no confusing hacks
  • What you see is what you get, understanding is easier
  • Productivity and consistency reflects on documentation as well

I can hear someone saying “But wait! Typing takes a bit longer with import statement, doesn’t it?”

I thought autocomplete feature was for that…

About the author

I am a software engineer. I held several different positions in different departments with several responsibilities from lead to senior supervisor capacities.

I approach problems with my out of the box perspective and make software development more productive, reliable, enjoyable while making folks happy. I deliver software products on time and budget, increase productivity by unprecedented margins with my consistent practices.

https://www.safakoner.com
https://twitter.com/safakoner
https://github.com/safakoner
Example API Reference
Contact Me

Leave a Reply

Your email address will not be published. Required fields are marked *