In computer science, the concept of namespaces is used all the time. But what exactly is a namespace, and why should you care?
In this article, I will try my best to explain exactly what a namespace is in plain English. In every example, I will use Python simply because it's the easiest to demonstrate with.
Every time you declare a variable, a function, or a class you declare a name. That name is stored inside the space it was created in. This space where names are stored is called a namespace. Whenever you want to use the variables, functions, or classes you simply refer to the name of it. If the name exists within the namespace it will be used to locate that object in memory. If it's not found withing the namespace, an exception will be raised.
# Variables a = 1 # Here the "a" name is declared as a variable b = 2 # Same with "b" print(a + b) # here the names a and b are referenced # Functions # The name "my_adder" is declared as a function def my_adder(a, b): return a + b print(my_adder(a,b)) # referencing the names declared earlier
To sum it up; a name is simply a reference to something in memory.
When defining a function, every variable declared inside the function is isolated from the outside of the function. Because of this, if a variable inside the function has the same name as a variable outside the function, the inside variable will be accessed when referenced.
a = 2 def my_scope(): a = 10 print("in my_scope, a is: "a) print("outside my_scope, a is: "a) my_scope() print("outside my_scope, a is still: "a)
python3 scope.py outside my_scope, a is: 2 in my_scope, a is: 10 outside my_scope, a is still: 2
Scope and namespace might seem similar, but if the "a" variable was not defined inside the function it would still be able to access the "a" variable from outside the function (as read-only).
What's special about scope is that it's impossible to access a variable declared inside a function from the outside.
To sum it up; Inside the scope of a function it's possible to access a name outside it, but it's impossible to access a name inside the function scope from outside it.
Adding external names to your namespace
Importing modules and packages enables us to add functionality to our Python scripts. The amazing thing about how importing in Python works is that you can be very specific on how you want to add the package or module to your namespace.
Let's first create a module called haxor.py that have three simple functions.
def hello(): print("Hello script kiddies!") def favorite_quote(): print("Mess with the best, die like the rest.") def internal_ref(): print("Internal stuff")
This added the haxor module to our namespace. To run anything from the module use the <module>.<name> syntax.
import haxor # Run imported functions from the haxor module haxor.hello() haxor.favorite_quote() haxor.internal_ref()
if we want to give the imported module another name/alias the "as" keyword can be used.
import haxor as h # Run functions from the aliased haxor module h.hello() h.favorite_quote() h.internal_ref()
To import a specific name directly into our namespace the "from ... import ..." syntax can be used. Please note that only the specified names will be imported.
from haxor import hello # Run the imported hello function from the haxor module hello() favorite_quote() # This will result in an exception internal_ref() # This will result in an exception
To import several names into your namespace you can comma separate each name you want to import.
from haxor import hello, favorite_quote # Run the imported functions from the haxor module hello() favorite_quote() # This will now run! internal_ref() # This will result in an exception
A much simpler, but at the same time much more dangerous solution is to import EVERYTHING from the module into the namespace.
from haxor import * # Run the imported functions from the haxor module hello() favorite_quote() internal_ref() # This will now run!
It might seem tempting to use the "from ... import *" statement because it will allow you to not prefix the function with a module name. let's run the last script from the previous chapter.
python3 all_import.py Hello script kiddies! Mess with the best, die like the rest. Internal stuff
Then let's change the script a little...
from haxor import * def internal_ref(): print("Yo!") hello() favorite_quote() internal_ref()
python3 woopsie.py Hello script kiddies! Mess with the best, die like the rest. Yo!
As you can read from the terminal dump above, the function definition in the script was called, not the one that was imported from the haxor module. This problem is called naming collision. The problem with naming collisions is that the name might be used for completely different means. By importing a module as a whole with the "import ..." syntax you preserve the namespace of the module, and will not experience any problems caused by naming collisions.
import haxor def internal_ref(): print("Yo!") haxor.hello() haxor.favorite_quote() internal_ref() haxor.internal_ref()
python3 better.py Hello script kiddies! Mess with the best, die like the rest. Yo! Internal stuff
As you can read from the script and the terminal dump above, the imported haxor module has a namespace that everything inside the module is placed inside. This prevents any naming collision.
The key takeaway is that when importing using the "from ... import *" syntax you don't know what you are importing into your namespace. For small modules with few names, this might not be a big deal. But if you import large modules with a lot of code this might cause unforeseen problems.
Please note that if you import using the "from ... import *" syntax you will also import variables as well. This is why PEP 8 (the Python style guide) does not recommend using the "from ... import *" syntax.
I hope this helped you appreciate the concept of namespaces 😉