I would still love to hear your feedback in the comments below. Enjoy!
When you start to work on even rudimentary Python application, the first thing you usually do is import some package you’re using. There are many ways to import packages and modules - some are extremely common (found in pretty much every Python file ever written) and some less so. In this post I will cover different ways to import or reload modules, some conventions regarding importing, import loops and some import easter-eggs you can find in Python.
Different Ways to Import
The basic Python import. The statement import foo looks for a foo model, loads it into memory and creates a module object called foo. How does Python knows where to find the foo module?
When a module named spam is imported, the interpreter first searches for a built-in module with that name. If not found, it then searches for a file named spam.py in a list of directories given by the variable
sys.pathis initialized from these locations:
- the directory containing the input script (or the current directory).
PYTHONPATH(a list of directory names, with the same syntax as the shell variable
- the installation-dependent default.
(from the documentation)
If there is a
bar object (which could be anything from a function to a submodule) it can be accessed like a member:
foo.bar. You can also import several modules in one line by doing
import foo, bar, but it is considered good practice to put each import in a single line.
foo.bar available without importing other stuff from
foo. The difference from
import foo is that if
foo also had a
baz member, it won’t be accessible.
from foo import bar
This statement imports
bar which could be anything that is declared in the module. It could be a function definition, a class (albeit a not-conventionally-named class) or even a submodule (which make foo a package). Notice that if
bar is a submodule of
foo, this statement acts as if we simply imported
bar (if it was in Python’s search path). This means that a
bar object is created, and its type is
foo object is created in this statement.
Multiple members of
foo can be imported in the same line like so:
from foo import bar, baz
The meaning of this is pretty intuitive: it imports both
baz from the module
baz aren’t neccessarily the same types: baz could be a submodule and bar could be function, for that matter. Unlike importing unrelated modules, it’s perfectly acceptable to import everything from one module in the same line.
from foo import *
Sometimes foo contains so many things, that it becomes cumbersome to import them manually. Instead, you can just import * to import them all at the same time. Don’t do this unless you know what you’re doing! It may seem convenient to just import * instead of specific members, but it is considered bad practice. The reason is that you are in fact “contaminating” your global namespace. Imagine that you do
import * on a package where someone unwittingly declared the following function:
When you do
import *, this list definition will override the global, built-in list type and you’ll get very, very unexpected errors. So it’s always better to know exactly what you’re importing. If you’re importing too much stuff from a certain package, you can either just suck it up or just import the package itself (import foo) and use the foo qualifier for every use. An interesting good use for
import * is in Django settings file hierarchy. It’s convenient there because you actually do want to manipulate the global namespace with imported settings.
from foo import bar as fizz
This one is far less common than what we covered so far, but still well known. It acts like
from foo import bar, except instead of creating a
bar object, it creates a
fizz module with the same meaning. There are two main reasons to use this kind of statement: the first is when you’re importing two similarly named objects from two different modules. You then use import as to differentiate them, like so:
The other reason, which I’ve seen used a few times is when you import a lone-named function (or class) and use it extensively throughout your code and want to shorten its name.
from .foo import bar
Well, this escalated quickly.
This one is pretty rare and a lot of people are completely unaware of it. The only difference in this statement is that it uses a modified search path for modules. Namely, instead of searching the entire
PYTHONPATH, it searches in the directory where the importing file lives. So if you have two files called
foo.py, you can use this import in fizz, and it will import the correct file, even if you have another
foo module in your
PYTHONPATH. What is this good for? Well, sometime you create modules with generic names like
common, but you might also have a
common package in the base of your project. Instead of giving different names, you can explicitly import the one closest to you. You can also use this method to load modules from an ancestor in the directory tree by putting several dots. For example,
from ..foo import Foo will search one directory up,
from ...foo import Foo will search two directories up, etc.
foo = __import__("foo")
Ever wondered how can you import a module dynamically? This is how. Obviously you wouldn’t use it with an explicit string, but rather with a variable of some kind. Also notice that you have to explicitly assign the imported module to a variable, or you won’t have access to its attributes.
This statement does exactly what it looks like. It reloads the
foo module. It’s pretty useful when you have a console open playing with a bit of code you’re tweaking and want to continue without resetting your interpreter.
Note: If you used from foo import bar, it’s not enough to reload
bar to update. You need to both reload
foo and call
from foo import bar again.
An import loop would occur in Python if you import two or more modules in a cycle. For example, if in
foo.py you would
from bar import Bar and in
from foo import Foo, you will get an import loop:
When this happens to you, the solution is usually to move the common objects from
bar.py to a different file (say
common.py). However, sometime there’s actually a real loop of dependencies. For example, a method in
Bar needs to create a
Foo instance and vice versa. When the dependency is in a limited scope, you should remember that you can use the
import command wherever you want. Putting imports at the top of the file is the common convention, but sometimes you can solve import loops by importing in a smaller scope, like in a method definition.
What fun would an easter egg be if you didn’t try it by yourself? Try these and have fun!Discuss this post at the comment section below.
Follow me on Twitter and Facebook