Lesson 1.6: Namespace, Importing
Namespaces
Namespaces are dictionaries that map names (variable names, function names, etc.) to values/functions.
Python maintains multiple namespaces (variable/function dictionaries) defined/accessed within the following scopes. Each of these namespaces is created at different moments and has different lifetimes.
Built-In: this is the highest scope; this scope contains Python keywords and built-in functions stored in the dictionary accessible as _builtin_ -
The built-in namespace is created when the Python interpreter starts up, and is never deleted.
Global: this is the next highest scope; this namespace is accessible as _main_ ; these names are always available
The global namespace for a module is created when the module definition is read in; normally, module namespaces also last until the interpreter quits.
Enclosed: this is a temporary scope that is the "next level up" from another local scope that occurs during the execution of nested functions; there may be multiple "enclosing" scopes during the execution of the function (when the function returns, these names are "dumped"); namespace lookup goes from the innermost to the outermost scope of nested functions (local scope/namespace is checked before enclosing scope/namespace)
Local: this is the scope associated with the execution of a function; this namespace is temporary and is available only during the execution of a function.
The local namespace for a function is created when the function is called, and deleted (really, forgotten) when the function returns or raises an exception that is not handled within the function.
Namespaces are completely isolated (from each other).
*You can have the SAME NAME (variable name, function name, etc.) stored SIMULTANEOUSLY in multiple namespaces WITH DIFFERENT VALUES/FUNCTIONS!!!
Python will never be confused about which variable name it should use because...
Python has a strict heirarchical process it goes through to map values to name (LEGB) ...
However, you may be confused about which namespace Python is going to access...
Because of this, you can introduce errors that are very difficult to find/debug.
Python Scope & Namespace Lookup Hierarchy
A scope is a textual region of a Python program where a namespace is directly accessible.
At any time during execution, there are several nested scopes whose namespaces are directly accessible:
When given a name, the Python interpreter searches from the "bottom" of the scoping hierarchy (i.e., the current location) to the "top".
The interpreter looks first in the local namespace (within the function's namespace).
Then it looks for it in any enclosing function namespaces.
Then it looks for it in the global scope (main namespace).
Finally, it looks for it in the builtins namespace (built in scope).
Image Source: http://sebastianraschka.com/Articles/2014_python_scope_and_namespaces.html
Some Important Concepts in Namespace
Objects: everything in Python (values, functions, etc.) is an object; much more on this tomorrow.
In Python, every object that is created is given a number that uniquely identifies it. It is guaranteed that no two objects will have the same identifier during any period in which their lifetimes overlap. Once an object’s reference count drops to zero and it is garbage collected, then its identifying number becomes available and may be used again.
Objects vs. Names: names and objects can interact in different ways
objects have individuality, and multiple names (in multiple scopes) can be bound to the same object
two different objects can have the same name (but not in the same scope)
Variable assignments do not copy data - they just bind names to objects (we already talked about this in data structures)
Deletions of variable names do not delete objects, they just remove the binding between name and object
the statement
del x
removes the binding ofx
from the namespace referenced by the local scope (i.e. deleteing x does not get rid of the dictionary it referred to - y remains bound to that object)
Importing: the basic idea of importing is to bring objects defined elsewhere (another scope) into the current namespace (or, binding a locally defined name with an object defined elsewhere - more to follow)
The normal way of business: variables in a function are local (exist only in the function)
The typical way to get around this is to pass in a reference to the object as a function argument
Interactions Between Scopes (Local, Global, Built-In)
It is possible to create/update a variable in the global scope (_main_ namespace) from within a function (although this can be dangerous to do).
This is done using the
global
keyword (see example below).
It is possible to use a variable defined at a higher level in the lookup hierarchy (without passing it as an explicit parameter to a function).
It is possible to "override" the built-in namespace (this is very bad!).
A list of all the built-in functions for Python 3.x are available here: https://docs.python.org/3/library/functions.html
It is a very bad idea to ever use names in your programs that exist in the built-in namespace (see reference above for what these are).
One frequently observed example is using the name "list" to hold a list you create, which overrides the function list in the
__builtin__
namespace.Any modules you load into your program (numpy, matplotlib, math, etc) will expect to be able to use the functions provided in
__builtin__
.The Python interpreter will find your name first, overriding the intended function, and many things will break.
You can always find out what variables are in the global scope in interactive mode.
This can be helpful during debugging sessions.
Most good debuggers also provide this capability (remember when we watched our code execute in Pycharm during the debugging demo?).
In interactive mode, the command to list all of the names in the _main_ namespace is: dir()
When you do this in Jupyter Notebook, you get some "extra" stuff associated with the notebook file, but all of the names that are currently active are in there (see below).
Note that the list below contains [ a, global_value, function_override, and local_func ] but does not contain local_value.
"local_value" exists only within the local scope that is created when local_func is evaluated.
removing items from the namespace with del
Importing modules, functions (into the current namespace)
Different Ways to Import Modules (and their effects)
import SomeModule
Example Import Statement:
import random
What Happens: you can now access any function in the random module by calling
random.desired_function
Example Use Case:
random.randint(1,10)
Strength: You get all functions in that module available for use; no "namespace pollution"; this is the recommended approach
Weakness: You have to specify which module/namespace (i.e. dictionary) to reference to use any function/variable/name from this module (i.e. the name is long because it is module.name)
import SomeModule as abbrv
Example Import Statement:
import numpy as np
Strength: Same as above but you define an abbreviation for the longer name to use as a prefix (instead of calling
SomeModule.desired_function
, you callabbrv.desired_function
)
from SomeModule import function1, function2, function3
Note: this approach often includes an "as" statement
Example Import Statement:
from matplotlib import pyplot as plt
Example Use Case: plt.hist(my_data)
Strength: you can now access functions/variables using a shorthand notation
Weakness: whatever names (variable names, function names) you import this way are now stored in the global scope, so they can't be used for anything else
from SomeModule import *
Example Import Statement:
from random import *
Example Use Case: randint(1,6)
Note: observe that there is no need to call random.randint
Strength: you save yourself some typing
Weakness: you may have just loaded/overwritten (many) names into your global namespace that you are unaware of; this often leads to "namespace pollution"; not recommended; don't do this
Let's take a look at each of these one at a time.
Import random and call the randint(1,6) function inside the module
Import the glob module and call the glob("*") function within the glob module
Module names can be abbreviated like this:
Import pylab as plt and evaluate the following code (write it out, don't copy):
We can also import specific functions within a module
Now we can do some fun things...
And now the bad example of what you shouldn't do...
Only use this when you are writing temporary quick and dirty code and even then, use sparingly.
Activity - Write a function for a line array and import the numpy package:
: number of sensors
: Wavelength
: distance between sensors
: Normalized bearing between -1,1 ()
Import numpy to use sin and
Activity
Import the scikit image package (package name: skimage)
Look up the different sub packages
Import two or three specific subpackages
Try getting some of the functions to work.