Structuring Packages
00:00
In the previous lesson, I showed you the variations on the import
statement. In this lesson, I’ll be diving deeper into how packages work in Python.
00:09
A package in Python is a directory containing a __init__
.py
file. When you import a package, submodules or subpackages do not get imported by default, but a __init__.py
file is still a Python script and one of the things you can do inside of it is import submodules or subpackages into the parent namespace.
00:29 You might do this to expose specific interfaces as an API to developers using your package without them having to know where the interface lives within the submodules of your package.
00:40
Let’s look at some packages to see how they work. In the top window, I’m showing the directory structure of a package named africa
. You can tell that it’s a package because it contains a __init__.py
file.
00:53
In this case, that file is empty, but having it there indicates to Python that this is a package. Inside the africa
directory is zimbabwe.py
, a Python file, which is a submodule inside the africa
package.
01:06
Let’s look at zimbabwe
. This module contains a hello world print()
statement. Alright, let me use the REPL and play with the package.
01:16
I’ll start by importing africa
. If you examine africa
in the REPL, Python tells you that it’s a module. Note that although I’ve got a package structure, what Python really is interested in is the __init__
.py
file.
01:31
The directory is almost secondary. Technically, you don’t load a package, you load a module in the package and that module, if the thing being imported, is the directory name, is the __init__
.py
file. Remember when I said submodules and subpackages don’t get loaded by default?
01:50
Although zimbabwe
exists inside africa
, it has not been imported. The __init__.py
file is empty. It doesn’t contain anything named zimbabwe
and hence you get an attribute error.
02:02
If I want zimbabwe
, I have to import it explicitly. And notice here that I didn’t import zimbabwe
, I imported africa.zimbabwe
.
02:16
So trying to use zimbabwe
on its own is a name error. If I use the fully qualified name, I can see that this is also a module. With me so far?
02:27 Let’s make it a little more complicated.
02:31
This is another package, this time called europe
. It is structured similarly to africa
in that it contains a __init__
.py
file and a single module.
02:40
But this time I’ve put some code in the __init__
.py
file. The code imports the greece
module into the namespace. The .
here is called a relative import and means to look for the module in the current directory structure.
02:56
This mechanism isn’t actually recommended and I’ll talk more about that later. Since __init__.py
gets run as part of the module loading process, importing europe
will have two side effects.
03:08
First, it imports greece
and second, it prints a message.
03:13
And this is greece.py
, which has its own message. Let’s try importing europe
.
03:22
When you import europe
, you can see that it imported greece.py
since importing that module, prints out the Greek message. Note that the inside europe.__init__.py
message comes after the Greek message because europe.__init__.py
imported greece.py
first.
03:39
And since europe
imported greece
, I can access the europe.greece
module. Note, I can’t get at greece
directly. I can do a different import to bring greece
in, but that’s not what I did here.
03:54 Alright, ready to make it more complicated?
03:59
The last package I’m going to show you is the americas
continent. It has a __init__.py
file which is empty like africa
’s was and a subpackage.
04:10
You can tell that north
is a subpackage because it also has a __init__.py
file and it has two modules, canada
and usa
.
04:19 As I’m Canadian myself, you can probably guess what jokes are coming next.
04:25
The __init__.py
in the north
subpackage is not empty. It imports canada
like how europe
did with greece
.
04:32 It also prints a helpful message.
04:36
This is the canada
module. What? You thought the joke would be anti-American? Nope. Us Canadians are, one, more polite than that and two, tend to prefer a self-deprecating flavor of humor.
04:48
That’s flavour and humour with a U. And finally, this is the usa
module. Did you know that although y’all is considered plural, it’s considered a small plural when addressing a particularly large group, you modify it with ‘all y’all’.
05:04
The hard part about that little fact is if you’re not American, you probably think I’m joking. Let’s import north
from the americas
package.
05:16
As north
’s __init__.py
file imports canada
, you get the message from the canada.py
module and the message from north
’s __init__.py
.
05:27
North
is a module and since I imported it using the from
syntax, I can use it directly even though its fully qualified name is americas.north
.
05:40
And because loading north
had the side effect of loading canada
, I can get at north.canada
as a module.
05:48
I cannot do the same for the usa
as, although it is a subpackage, it hasn’t been imported.
05:57
I also can’t import north.usa
as that isn’t the name of the module.
06:07
I have to use the fully qualified name in order to get it. And like before, because I imported the fully qualified module name, I can’t use usa
on its own.
06:19
But interestingly, I can now reference north.usa
. Loading usa
has put it in the namespace and north
is already there, so Python knows how to reference this now. I can also get at it using the fully qualified form.
06:35 Namespaces are all about trying to reduce your name conflicts, so what happens when your namespaces conflict? Next up, I’ll show you module shadowing.
Become a Member to join the conversation.