04 Introduction to the importing system of Python

Until now, all our code was in the single file. Also we use only built-in “commands” that are defined in the global scope, but as you can imagine it is not practical to put everything in the global scope, some namespace convention should be imposed. Also as long as complexity of your code increase, you want to split it up into different files.

Let’s look on some examples:

{01_import1.py}

This is variation of the same program from previous chapter, but here, we don’t use some “hard-coded” values, we use some random int number in [1,9]. If you will run program multiple times, you will get different questions.

Here, the interesting is the first line. random is a name of (built-in) package that defines special “command” randint. We’re importing reference to randint object from random package and we’re creating new reference named randomInt in the global scope. So, after line 1, we can write randomInt as if it was available in the global scope to begin with.

Another variance:

{02_import2.py}

It almost the same as example above, but we didn’t specified “as what” we want to import the reference. In this case the same name is used. In our global scope we will reference with the name randint that will point to the same object as random.randint object.

Note: Notation random.randint is called dot notation.

In order to understand this notation we will look on another variant:

{02_import2.py}

Here we do something a bit different. We’re creating variable with name random that point to entire package.

Note: Importing the module triggers “initialization code”. It is as if interpreter “put on holds” the execution of our code, and start execute the “global” code of random module. And when done, it will come back to us.

Now, we can use “dot notation” to access “command” that are defined in random "namespace" as we see in line 3 and 4 random.randint().

Creating you own package

Now, lets see how you can create you own package. Here is example of creating and using package with the simplemathexample.

Directory Structure

Minimum requirement to define package in Python is to create directory with the package name and put empty __init__.py file. We put print(‘simplemathexample’) in order to see when it will be printed.

Now, inside out package we created module (this is just ordinary Python file) — named const.py. Inside it,we’re defining FIRST and SECOND as constant (using random package). This is the content of const.py:

{const.py}

Note: const.py and __init__.py should be in directory named as package name (simplemathexample). The file you run should be directly outside the simplemathexample directory, see above.

Note: Capital letters are usually used of constants.

{04_import4.py}

If we run 04_import4.py (for example, by going to directory where 04_import4.py is locating and typing python 04_import4.py), we will get output something like this:

__main__
simplemathexample
simplemathexample.const
04_import4
How much is 6 + 6 ? 12
You're right!

Every module/file has in it’s global scope (yes, global scope is per file and not per whole program) has special variable with name __name__ (it has 2 underscores at the beginning and 2 underscores at the end). 2 underscores or dunders for short, means that this variable has some special treatment in Python.

For files 04_import4.py, __init__.py, cons.py I've adding print(__name__) statement at the beginning of the file.

First file that is running has __name__==__main__ If we want to know whether we’re first file or we was imported, we can check if __name__==__main__. This is standard idiom, I will use it later in the book.

Than package is initialized from the top simplemathexample to buttom simplemathexample.const. After initialization is completed, we’re going to line 2 of the __main__ file, namely 04_import4.py.

Avoid bad practice

{05_import5.py}

The bad practice is at the first line. from … import * means import everything. If there some auxiliary “commands” such as random that was put to simplemathexample.const's module global, it will be re-imported to us. Usually, this not what we do. We do want to get ONE and TWO, but we don’t want to take random. That is exactly the reason why I put underscore before the name and used _random.

Note: Variables with underscore are not imported. There are also another technique than this to “hide the information” about how ONE and TWO was created. May be, we’re calling some OS-specific API to get random number or may be there is call over the internet to get them. For the 05_import5.py it shouldn’t be matter. The exact details how simplemathexample.const is implemented shouldn’t affect another part of the program, we should be able to make local change (only in const.py file) and no other module should require any change.
Information hiding is actually term from computer science. I will talk about this more throught the book.

So, from line 1 we get only ONE and TWO. We can however, still import _random "command” if really want too (see line 2). There is nothing to prevent this.

Take-away: don’t use from … import *

Note: There are many techniques on how to write your package in order to avoid “abuse”, but they out of scope for this book.

In the next chapter we will talk about Primitive and built-in types.

This story is part of my new book.

--

--

Senior Software Engineer at Pursway

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store