Namespaces

What will we cover?

Introduction

What's a namespace? I hear you ask. Well, it's kinda hard to explain. Not because they are especially complicated, but because every language does them differently. The concept is pretty straightforward, a namespace is a space or region, within a program, where a name (of a variable, function, class etc) is valid. We actually use this idea in everyday life. Suppose you work in a big company and there is a colleague called Joe. In the accounts department there is another guy called Joe who you see occasionally but not often. In that case you refer to your colleague as "Joe" and the other one as "Joe in Accounts". You also have a colleague called Susan and there is another Susan in Engineering with whom you work closely. When referring to them you might say "Our Susan" or "Susan from Engineering". Do you see how you use the department name as a qualifier? That's what namespaces do in a program, they tell both programmers and the translator which of several identical names is being referred to.

They came about because early programming languages (like BASIC) only had Global Variables, that is, ones which could be seen throughout the program - even inside functions. This made maintenance of large programs difficult since it was easy for one bit of a program to modify a variable without other parts of the program realizing it - this was called a side-effect. To get round this, later languages (including modern BASICs) introduced the concept of namespaces. (C++ has taken this to extremes by allowing the programmer to create their own namespaces anywhere within a program. This is useful for library creators who might want to keep their function names unique when mixed with libraries provided by another supplier.)

Another term used to describe a namespace is scope. The scope of a name is the extent of a program in which that name can be unambiguously used, for example inside a function or a module. A name's namespace is exactly the same as it's scope. There are a few very subtle differences between the terms but only a Computer Scientist pedant would argue with you, and for our purposes namespace and scope are identical.

Python's approach

In Python every module creates it's own namespace. To access those names we have to either precede them with the name of the module or explicitly import the names we want to use into our module's namespace. Nothing new there, we've been doing it with the sys and time modules already. (A class definition also creates its own namespace. Thus, to access a method or property of a class, we need to use the name of the instance variable or the class name first. We'll talk a lot more about that in the OOP topic.)

In Python there are a total of five possible namespaces (or scopes):

  1. Built in scope - names defined within Python itself, these are always available from anywhere in your program.
  2. Module scope - names defined, and therefore visible within a file or module. Confusingly, this is referred to as global scope in Python whereas global normally means visible from anywhere, in other languages.
  3. Local scope - names defined within a function or a class method (including any parameters))
  4. Class scope - names defined inside classes, we'll touch on these in the OOP topic.
  5. Nested scope - a slightly complex topic which you can pretty much ignore for now!

Let's take a look at a piece of code that includes examples of the first three:

def square(x):
   return x*x

data = int(input('Type a number to be squared: '))
print( data, 'squared is: ', square(data) )

The following table lists each name and the scope to which it belongs:

Name Namespace
square Module/global
x local (to square)
data Module/global
int built-in
input built-in
print built-in

Note that we don't count def or return as names because they are keywords or, part of the language definition, if you try to use a keyword as the name of a variable or function you will get an error.

So far so good. Now how does this come together when variables in different namespaces have the same name? Or when we need to reference a name that is not in the current namespace?

Accessing Names outside the Current Namespace

Here we look in more detail at exactly how Python locates names, even when the names we are using are not in the immediate namespace. It is resolved as follows, Python will look:

  1. within it's local namespace (the current function) or the enclosing function or class, if it's a nested function or method,
  2. within the module scope (the current file),
  3. the built-in scope.

But what if the name is in a different module? Well, we import the module, as we've already seen many times in the tutorial. Importing the module actually makes the module name visible in our module namespace. We can then use the module name to access the variable names within the module using our familiar module.name style. This explains why, in general, it is not a good idea to import all the names from a module into the current file: there is a danger that a module name will be the same as one of your variables and one of them will mask the other causing strange behavior in the program.

For example let's define two modules, where the second imports the first:

##### module first.py #########
spam = 42
def print42(): print( spam )
###############################
##### module second.py ########
from first import *  # import all names from first

spam = 101   # create spam variable, hiding first's version
print42()    # what gets printed? 42 or 101?

################################

If you thought it would print 101 then you were wrong (and I admit I expected that the first time I tried something similar!). The reason why it prints 42 instead, has to do with the definition of a variable in Python, as we described it away back in the Raw Materials topic. Recall that a name is simply a label used to reference an object. Now in the first module the name print42 refers to the function object defined in the module (if this sounds odd there's more explanation in the advanced topic Functional Programming where it discusses something called a lambda expression). So although we imported the name into our module we did not import the function, which still refers to its own module's version of spam. Thus when we created our new spam variable it has no effect on the function referred to by the name print42. Here is a diagram showing how it works for both types of import. Notice how the second import duplicates the name print42 from first.py into second.py:

namespaces diagram

All of that confusion should serve to illustrate why, although it's more typing, it is much safer to access names in imported modules using the dot notation. There are a few modules, such as Tkinter which we'll meet later, which are commonly used by importing all of the names, but they are written in such a way to minimize the risk of name conflicts, although the risk always exists and can create very hard to find bugs.

Finally there is another safe way to import a single name from a module, like this:

from sys import exit

Here we only bring the exit function into the local namespace. We cannot use any other sys names, not even sys itself!

Avoiding Name Clashes

If a function refers to a variable called X and there exists an X within the function (local scope) then that is the one that will be seen and used by Python. It's the programmer's job to avoid name clashes such that a local variable and module variable of the same name are not both required in the same function - the local variable will mask the module name.

There is no problem if we just want to read a global variable inside a function, Python simply looks for the name locally and, not finding it, will then look globally (and if need be at the built-in namespace too). The problem arises when we want to assign a value to a global variable. That would normally create a new local variable inside the function. So, how can we assign a value to a global variable without creating a local variable of the same name? We can achieve this by use of the global keyword:

var = 42
def modGlobal():
   global var  # prevent creation of a local var
   var = var - 21

def modLocal():
   var = 101
   
print( var )   # prints 42
modGlobal()
print( var )  # prints 21
modLocal()
print( var )  # still prints 21

Here we see the global variable being changed by the modGlobal function but not changed by the modLocal function. The latter simply created its own internal variable and assigned it a value. At the end of the function that variable was garbage collected and its existence was unseen at the module level.

In general you should minimize the use of global statements, it's usually better to pass the variable in as a parameter and then return the modified variable. Here is the modGlobal function above rewritten to avoid using a global statement:

var = 42
def modGlobal(aVariable):
    return aVariable - 21

print( var )
var = modGlobal(var)
print( var )

In this case we assign the return value from the function to the original variable while also passing it in as an argument. The result is the same but the function now has no dependencies on any code outside itself - this makes it much easier to reuse in other programs. It also makes it much easier to see how the global value gets changed - we can see the explicit assignment taking place.

We can see all of this at work in the next example, which does nothing very useful, it is purely about illustrating the points made so far! Work through it until you are comfortable with exactly which name and value is being used at each step. :

# variables with module scope
W = 5
Y = 3
 
#parameters are like function variables 
#so X has local scope
def spam(X):
    
   #tell function to look at module level and not create its own W
   global W
   
   Z = X*2 # new variable Z created with local scope
   W = X+5 # use module W as instructed above

   if Z > W:
      # pow is a 'builtin-scope' name
      print( pow(Z,W) )
      return Z
   else:
      return Y # no local Y so uses module version

print("W,Y = ", W, Y )
for n in [2,4,6]:
   print( "Spam(%d) returned: " % n, spam(n) )
   print( "W,Y = ", W, Y )

VBScript

VBScript takes a fairly straightforward approach to scoping rules: if a variable is declared outside a function or subroutine then it is globally visible, if a variable is declared inside a function or subroutine it is local to that module (and hides any global variable with the same name). The programmer is responsible for managing all naming conflicts that might arise. Because all VBScript variables are created using the Dim statement there is never any ambiguity about which variable is meant as is the case with Python.

There are some slight twists that are unique to web pages, namely that regardless of <script> tag boundaries global variables are visible across an entire file, not just within the <script> tag in which they are defined.

We will illustrate those points in the following code:

<script type="text/vbscript">
Dim aVariable
Dim another
aVariable = "This is global in scope"
another = "A Global can be visible from a function"
</script>

<script type="text/vbscript">
Sub aSubroutine
   Dim aVariable
   aVariable = "Defined within a subroutine"
   MsgBox aVariable
   MsgBox another  ' uses global name
End Sub
</script>

<script type="text/vbscript">
MsgBox aVariable
aSubroutine
MsgBox aVariable
</script>

There are a couple of extra scoping features in VBScript that allow you to make variables accessible across files on a web page (e.g from an index frame to a content frame and vice-versa). However we won't be going into that level of web page programming here so I'll simply alert you to the existence of the Public and Private keywords.

And JavaScript too

JavaScript follows much the same rules, variables declared inside a function are only visible within the function. Variables outside a function can be seen inside the function as well as by code on the outside. As with VBScript there are no conflicts as to which variable is intended because variables are explicitly created with the var statement.

Here is the equivalent example as above but written in JavaScript:

<script type="text/javascript">
var aVariable, another;  // global variables
aVariable = "This is Global in scope<BR>";
another = "A global variable can be seen inside a function<BR>";

function aSubroutine(){
   var aVariable;        // local variable
   aVariable = "Defined within a function<BR>";
   document.write(aVariable);
   document.write(another);  // uses global variable
}

document.write(aVariable);
aSubroutine();
document.write(aVariable);

</script>

This should, by now, be straightforward.

Things to Remember

Previous  Next