Table of Contents
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 need to even 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…