|What will we cover?|
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 (variable, 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 whereby 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.
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 modules namespace. Nothing new there, we've been doing it with the sys and time modules already. (In a sense 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. More about that in the OOP topic)
In Python there are a total of four possible namespaces (or scopes):
Let's take a look at a piece of code that includes examples of all of these (except nested):
def square(x): return x*x data = int(raw_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:
|x||local (to square)|
Note that we don't count def and print as names because they are keywords or commands forming part of the language, if you try to use them as names of variables of functions 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?
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:
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 variable will have the same name 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 when I first wrote the example!). 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 actually import the function which still refers to its own version of spam. Thus when we created our new spam variable it has no effect on the function referred to by print42
All of that confusion should serve to illustrate why, although it's more typing, it is much safer to access names in foreign 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!
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 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 this example (which is purely about illustrating the point!):
# 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
VBScript takes a fairly straightforward approach to scoping rules: if a variable is outside a function or subroutine then it is globally visible, if a variable is inside a function or subroutine it is local to that module. 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 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.
This should, by now, be straightforward.
|Things to Remember|
If you have any questions or feedback on this page send me mail at: email@example.com