Introduction
Python (at least IMO) is a very nice programming language, based on really powerful fundamentals. In this post, I don’t want to compare Python to other programming languages or value it. However, when it comes to the fundamentals of a programming language, Python has some interesting constructs which can differ from other languages. Namespaces are one of those constructs, which can be somewhat difficult to understand if you’re new to Python or when you’re coming from another programming language (e.g. C or Java).
Before we’re digging into Python namespaces, I want to clarify that I’m not that hardcore developer. This is just my attempt to explain Python namespaces to someone who recently started developing Python or is switching from another programming language.
Someone recently asked me:
What are Python namespaces and why do I need them?
To explain it in one single sentence and bring it right to the point:
Python uses namespaces to implement scoping.
Now, I’m aware that sounds somewhat fuzzy, so I try to explain that step by step. To learn more about namespaces, you need to know about encapsulation, scoping and names itself. So, let’s go!
What is a name?
If you start developing Python, you immediately come in contact with its dynamic nature. Because of this awesome feature, you can basically name everything in Python. For example and to keep it simple, you can name a string, number, boolean or list:
my_string = 'spam and eggs' my_number = 42 my_boolean = True my_list = ['spam', 'eggs']
Technically these are all objects, but that’s not important right now. Though, because of these fundamentals, you cannot only name simple data types, but you can also name functions:
def spam(): return 'SPAM SPAM SPAM' eggs = spam print(spam()) # SPAM SPAM SPAM print(eggs()) # SPAM SPAM SPAM
Please note that we didn’t use parenthesis when we were assigning the function spam to the eggs variable. This is really important, as the spam() function is not called at the time of the variable assignment. The following example shows the difference:
def spam(): return 'SPAM SPAM SPAM' print(spam) # <function spam at 0x123456789> print(spam()) # SPAM SPAM SPAM
As I mentioned before, everything in Python is an object. So, to keep it (really) simple, a variable in Python is just a name or a label which points to an object. But don’t mess up that Python name pointer thingy with memory pointers like you’ll find in C.
What is a namespace?
Obviously, a namespace is a space of names. If you’ve red the official Python tutorial, you’ll find the following definition:
A namespace is a mapping from names to objects.
Well, that’s true and this is exactly what a namespace is. It holds names (aka variables) which map to an object. Of course you can also have multiple names per object:
Here’s an example to show that both names point to the same object (in this case a str object):
spam = 'spam and eggs' eggs = spam print(spam) # spam and eggs print(eggs) # spam and eggs print(id(spam)) # 4476916512 print(id(eggs)) # 4476916512
As you can see, namespaces are created automatically by Python. Names can explicitly be defined or they can be imported from other Python modules. I’ll explain that below.
Encapsulation and scoping
In object oriented programming, there’s something called encapsulation, which includes the construct whereas data is bundled with the classes, objects, methods and functions operating on that data. If you read about encapsulation in Python, you’ll often read about namespaces, scopes and finally Python modules.
Now, keep it simple and let’s say a Python module is simply a file containing some Python code. Each Python module has its own global and isolated namespace. Of course you can’t have two identically named functions in one single namespace (Python module), as you’ll have a name clash. However, you can have the same function name defined in two different Python modules, as each module will have it’s own isolated namespace.
This whole “magic” is called scoping, and it’s implemented on several levels. You’ll always have your global module namespace, because of that you can do things like:
spam = 'spam and eggs' print(spam)
Functions have a separate namespace:
def spam(): eggs = 'spam and eggs' print(eggs) spam() # spam and eggs print(eggs) # raises a NameError exception
As you can see, you can access the eggs variable within the spam() function, but not outside of it. This is, because eggs was defined in the function’s namespace and is only available in there.
Said that, it’s important to know that namespaces are searched from inside out. This means functions can access the global namespace, but not vice-versa. Thus, if a function can’t find the name in its local namespace, it will try lookup the name in the global namespace:
def spam(): print(eggs) eggs = 'spam and eggs' spam() # spam and eggs
The spam() function is now accessing the eggs variable from the global namespace, because there’s no eggs variable in its local namespace.
With classes and objects it gets a bit more complicated. However, here’s a (really) simple example:
class Meal: def __init__(self): self.eggs = 2 my_meal = Meal() print(my_meal.eggs) # 2 print(eggs) # raises a NameError exception
As you can see in the first print line, you can only access the object’s namespace by using the object’s name my_meal.
However, you can’t access the eggs property directly, as it’s only available in the object’s namespace and not in the global namespace.
LEGB
When you read about names, namespaces or scoping in Python, you might see the term LEGB. As you can see, the term is built from 4 different letters, which simply stand for the 4 different Python scopes:
- Local – Names which are assigned within a function
- Enclosing – Names which are assigned in a closure (function in a function)
- Global – Names which are assigned at the top-level of a module, for example on the top-level of your Python file
- Built-in – Names which are standard Python built-ins, such as open, import, print, return, Exception
Now as we know the different scopes, we need some rules:
- The lowest of the scopes is local, therefore the highest is the built-in
- Lower scopes can always access names in higher scopes
- Higher scopes can never (directly) access names in lower scopes
This gives us the following graph:
As we now know the scopes and the rules applying for these scopes, we can have a look at some sample code:
Please note, I won’t cover the enclosed scope here, as I’ll introduce you to enclosures in another blog post.
Imports
I’ve already described Python modules (really poorly) before. Now, I’ve told you that each Python module has its own global namespace. But a Python module is pretty useles if you don’t import it, thus we need to have a look at imports. There are several ways to import a Python module and each way will have a different effect.
For the following example, let’s say we’ve a simple Python module called spam.py with the following content:
my_string = 'spam and eggs' my_number = 42
Import spam
Here’s the recommended way to import your whole module into the own global namespace:
import spam print(spam.my_string) # spam with eggs print(spam.my_number) # 42
As you can see, you can access the module’s namespace by using the module’s name as prefix. This means, the names of the imported Python module are not in our global namespace. Only the imported Python module itself is in our global namespace.
From spam import my_string
You can also import specific names from the Python module into the own global namespace:
from spam import my_string print(my_string) # spam with eggs print(my_number) # raises a NameError
As you can see, the my_string name was imported while my_number is not available in the own global namespace.
From spam import *
Here’s a way to import all names of your module directly into the own global namespace:
from spam import * print(my_string) # spam with eggs print(my_number) # 42
As you can see, all names of the spam module are available in the own global namespace, thus no prefix is required.
Please note, that by default all names from a module will be imported when using the from module import * syntax. However, you can control which names should be imported via the __all__ list.
So, let’s modify the spam module a bit and add the __all__ list:
__all__ = ( 'my_string', ) my_string = 'spam and eggs' my_number = 42
Now, if you do the same import as above, you’ll see the full effect of the changes above:
from spam import * print(my_string) # spam with eggs print(my_number) # raises a NameError
As you can see, the asterisk * now only includes the my_string name, because my_number isn’t included in the __all__ list.
25 Comments