Python Fundamentals Tutorial: Code Organization

9. Code Organization

In this section, we’ll cover some of the tools you need as your scripts get to be bigger than just the contents of a single file. Namely code organization, command-line arguments and file I/O.

9.1. Namespaces

Inside a single python module (file) there are multiple namespaces. The global namespace represents the full contents of the file, while inside each function there is a local namespace. The contents of these namespaces can be accessed as dictionaries using the functions globals() and locals() respectively.

When objects (variables, functions, etc) are defined in the file, they manipulate the global namespace. Anything defined inside a function manipulates its local namespace. Notice in the example below that the namespace is manipulated as the file is read. In other words, the first print statement occurs before the interpreter is aware of the presence of the function definition.

[Note]Note

The following example makes use of import, which will be explained in the next section. It also uses pprint.pformat which converts a dictionary into a string in a manner that is more easily read when printed.

organization-1-namespaces.py. 

'''Some documentation for this file.'''
import pprint

print 'globals before def: %s\n' % pprint.pformat(globals(), indent=4)

def simple():
  print 'locals before a: %s\n' % locals()
  a = 'simple'
  print 'locals after a: %s\n' % locals()
  return a

print 'globals after def: %s\n' % pprint.pformat(globals(), indent=4)

simple()

$ python organization-1-namespaces.py
globals before def: {   '__builtins__': <module '__builtin__' (built-in)>,
    '__doc__': 'Some documentation for this file.',
    '__file__': 'samples/organization-1-namespaces.py',
    '__name__': '__main__',
    '__package__': None,
    'pprint': <module 'pprint' from '/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pprint.pyc'>}

globals after def: {   '__builtins__': <module '__builtin__' (built-in)>,
    '__doc__': 'Some documentation for this file.',
    '__file__': 'samples/organization-1-namespaces.py',
    '__name__': '__main__',
    '__package__': None,
    'pprint': <module 'pprint' from '/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pprint.pyc'>,
    'simple': <function simple at 0x100425f50>}

locals before a: {}

locals after a: {'a': 'simple'}

9.2. Importing modules

Have another look at an example similar to the one above. Notice that the modules that are imported are present in the global namespace.

organization-2-imports.py. 

import collections
import pprint

d = collections.deque()
d.append('a')
d.appendleft('b')

pprint.pprint(globals())

$ python organization-2-imports.py
{'__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'samples/organization-2-imports.py',
 '__name__': '__main__',
 '__package__': None,
 'collections': <module 'collections' from '/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/collections.pyc'>,
 'd': deque(['b', 'a']),
 'pprint': <module 'pprint' from '/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pprint.pyc'>}

Objects from this module are accessed using dotted notation.

Alternatively, you can import the specific element from the module, using the from ... import syntax.

organization-3-import-submodule.py. 

from collections import deque
from pprint import pprint

d = deque()
d.append('a')
d.appendleft('b')

pprint(globals())

$ python organization-3-import-submodule.py
{'__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'samples/organization-3-import-submodule.py',
 '__name__': '__main__',
 '__package__': None,
 'd': deque(['b', 'a']),
 'deque': <type 'collections.deque'>,
 'pprint': <function pprint at 0x100435578>}

It is also possible to import an entire namespace. This should be done with caution, as it can lead to unexpected elements in your namespace and conflicts in naming.

organization-4-import-all.py. 

from collections import *
from pprint import pprint

d = deque()
d.append('a')
d.appendleft('b')

pprint(globals())

$ python organization-4-import-all.py
{'Callable': <class '_abcoll.Callable'>,
 'Container': <class '_abcoll.Container'>,
 'Hashable': <class '_abcoll.Hashable'>,
 'ItemsView': <class '_abcoll.ItemsView'>,
 'Iterable': <class '_abcoll.Iterable'>,
 'Iterator': <class '_abcoll.Iterator'>,
 'KeysView': <class '_abcoll.KeysView'>,
 'Mapping': <class '_abcoll.Mapping'>,
 'MappingView': <class '_abcoll.MappingView'>,
 'MutableMapping': <class '_abcoll.MutableMapping'>,
 'MutableSequence': <class '_abcoll.MutableSequence'>,
 'MutableSet': <class '_abcoll.MutableSet'>,
 'Sequence': <class '_abcoll.Sequence'>,
 'Set': <class '_abcoll.Set'>,
 'Sized': <class '_abcoll.Sized'>,
 'ValuesView': <class '_abcoll.ValuesView'>,
 '__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'samples/organization-4-import-all.py',
 '__name__': '__main__',
 '__package__': None,
 'd': deque(['b', 'a']),
 'defaultdict': <type 'collections.defaultdict'>,
 'deque': <type 'collections.deque'>,
 'namedtuple': <function namedtuple at 0x100425ed8>,
 'pprint': <function pprint at 0x1004355f0>}

9.3. Creating Modules

Once your code starts to get bigger than a script, you will want to start organizing it into modules. Unlike some other languages (Java for example) each file in Python is a module. Directories can also be used as a further layer of organization with some care.

Using the file-only model, functions can be created in another file in the same directory (or somewhere in the $PYTHONPATH) and imported using the filename and the function name.

tools.py. 

def shorten(toolong):
    return toolong[:2]

complexity-4-file-module.py. 

from tools import shorten

print shorten('abcdef')

$ python complexity-4-file-module.py
ab

As code starts to require even more organization, perhaps with multiple types of utility functions, this file could be moved to a subdirectory. In order to use a directory as a module, it is required that the special file __init__.py be present in the directory, which can be empty.

$ ls tools2
tools2/__init__.py
tools2/strings.py

tools2/strings.py. 

def shorten(toolong):
    return toolong[:2]

complexity-5-directory-module.py. 

from tools2 import strings

print strings.shorten('abcdef')

$ python complexity-5-directory-module.py
ab