Loading video player…

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.