Modules and Scoping Rules

Modules are used to organize larger Python projects, the Python standard library is split into modules to make it more managable, if you are creating a large preoject then it would be a good idea to split it up into modules.

A module is a file containing code, it groups related functions and variables and can be used by yourself or even other programmers to use. Python uses namespacing to distinguish methods names from different modules so that you might use a specific method name that is used by another programmer, I will discuss namespacing in detail later in this section.

The bottomline is that Modules make Python more managable and I am going to discuss them in more detail in this section. When a module is imported for the first time syntax checking will be actioned on the module and any errors will be displayed and the program terminated.

Basic module example
#### mymath.py (Module file) ####
def square(i):
    return i * i


def cubed(i):
    return i * i * i


def area(i):
    return(3.14159 * i * i)
using from to import module
#### testMymath.py (use the module) ####
from mymath import square                         # import the square method from module mymath
from mymath import cubed                          # import the cubed method from module mymath
from mymath import area                           # import the area method from module mymath
from mymath import area as paul                   # you can name the method anything you like, it just a reference to the area method

print( square(5) )                                # call the modules square method
print( cubed(5) )                                 # call the modules cubed method
print( area(5) )                                  # call the modules area method

print( paul(5) )                                  # you are actually calling the area method
using from wildcard to import module
#### testMymath2.py (use the module) ####
from mymath import *                              # you can use a wildcard to import all methods and variables

print( square(5) )                                # call the modules square method
print( cubed(5) )                                 # call the modules cubed method
print( area(5) )                                  # call the modules area method
using import to import module
#### testMymath3.py (use the module) ####
import mymath as mymath                           # you can use your own label

print( mymath.square(5) )                         # call the modules square method, you must add the module name then method
print( mymath.cubed(5) )                          # call the modules cubed method
print( mymath.area(5) )                           # call the modules area method

Module Search Path and Private Names

Python looks for modules in a defined variable called path which you can display using the below, the first module found will be used, the operating system variable is called PYTHONPATH which can be used to add additonal paths (directories) where modules are located. If you use an empty string in the path this means current directory. You can also specify a location using the dot notation again seen in an example below

Module search path
import sys

sys.path
Specify path
import testing.mymath

Note this will look from the current directory in to testing directory - ./testing/mymath.py, you can also 
add this path to the PYTHONPATH variable and will save a bit of typing

You an also create a file with a .pth extension listing the paths that you want to add

Create .pth file
mymodules
c:\Users\pvalle\My Documents\python\modules

Note: the above directories will be add to sys.path

If you don't want a method or variable to be imported use a underscore for the name, this only works when using a wilcard (*)

hide methods or variables
#### mymath.py ####
def _private():
    return "This is private"
    
#### testMymath2.py ####
from mymath import *

print( _private() )                     # ERROR - _private won't be defined as it's hidden

Scoping Rules and Namespaces

A namespace in Python is a mapping from identifiers to objects, Python uses this to keep track of variables, etc. There are three namespaces

Simply put, a namespace is a collection of names. In Python, you can imagine a namespace as a mapping of every name you have defined to corresponding objects. Each module creates it's own namespace, these different namespaces are isolated. Hence, the same name that may exist in different modules do not collide. Modules can have various functions and classes. A local namespace is created when a function is called, which has all the names defined in it. So when naming and adding to the path becareful that you are not overriding and existing method or variable, otherwise it can get confusing.

Tools to diagnose namespacing issues
import math
import mymath

def method1(lastname):
    firstname = "Paul"
    return firstname + lastname

print(globals())

del math                            # we can remove math bindings
print(locals())                     # this will now be different to globals

print( dir() )                      # return the module names added to the local namespace

print(max.__doc__)                  # you can obtain documentation from modules