Scope and Modules

Scope and Modules#

Namespaces#

  • Python programs are constructed from code blocks

    • Note: cells in a Python notebook are not blocks

  • Examples of code blocks in Python

    • A function body

    • A module

  • A namespace is a mapping (like a dictionary) from variable names to objects

    • Each block is associated with a namespace

  • Types of namespaces in Python:

    • Local namespace

    • Enclosing namespace

    • Global namespace

    • Built-ins namespace

  • A built-ins namespace is created at the beginning of each program and is never deleted

  • A global namespace is created for every Python program and module

    • __main__ is the default module, all code that does not belong to any named module is made part of this

    • Created when the module is imported (i.e., read in) and normally lasts until the interpreter is quit

  • A local namespace is created for a function whenever it is called and destroyed when it returns

Scope#

  • Scope is the code region where a specific namespace is accessible

    • Defines the visibility of a variable name within a block

    • Allows for reuse of variable names in different blocks

  • When accessing a variable through its name, Python interpreter searches, in order,

    1. Local namespace

    2. Inherited enclosing local namespace(s)

    3. Global namespace

    4. Built-ins namespace

_images/scope.png
def somefunction():
    x = 3
    return x
x = 4
#def print(x):
#    return x*2
type(print)
builtin_function_or_method
  • dir(__builtins__)

    • Shows the variable names in the built-ins namespace

  • globals()

    • Returns a dictionary with variable names for the keys and the objects they refer to as values

  • locals()

    • Returns a dictionary with local variable names and their objects in the current scope

  • dir()

    • Returns the variable names in the local namespace as a list object

def f():
    pass
globals()
{'__name__': '__main__',
 '__doc__': 'Automatically created module for IPython interactive environment',
 '__package__': None,
 '__loader__': None,
 '__spec__': None,
 '__builtin__': <module 'builtins' (built-in)>,
 '__builtins__': <module 'builtins' (built-in)>,
 '_ih': ['',
  'def somefunction():\n    x = 3\n    return x\nx = 4\n#def print(x):\n#    return x*2\ntype(print)',
  'def f():\n    pass\nglobals()'],
 '_oh': {1: builtin_function_or_method},
 '_dh': [PosixPath('/home/runner/work/EngineeringComputations/EngineeringComputations/engineering_computations')],
 'In': ['',
  'def somefunction():\n    x = 3\n    return x\nx = 4\n#def print(x):\n#    return x*2\ntype(print)',
  'def f():\n    pass\nglobals()'],
 'Out': {1: builtin_function_or_method},
 'get_ipython': <bound method InteractiveShell.get_ipython of <ipykernel.zmqshell.ZMQInteractiveShell object at 0x7f68fc10b7d0>>,
 'exit': <IPython.core.autocall.ZMQExitAutocall at 0x7f68fc11bd50>,
 'quit': <IPython.core.autocall.ZMQExitAutocall at 0x7f68fc11bd50>,
 'open': <function io.open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)>,
 '_': builtin_function_or_method,
 '__': '',
 '___': '',
 '_i': '\ndef somefunction():\n    x = 3\n    return x\nx = 4\n#def print(x):\n#    return x*2\ntype(print)',
 '_ii': '',
 '_iii': '',
 '_i1': '\ndef somefunction():\n    x = 3\n    return x\nx = 4\n#def print(x):\n#    return x*2\ntype(print)',
 'somefunction': <function __main__.somefunction()>,
 'x': 4,
 '_1': builtin_function_or_method,
 '_i2': 'def f():\n    pass\nglobals()',
 'f': <function __main__.f()>}
  • Examples

def hello():
    name = input("Enter your name: ")
    greeting = "Hello " + name + "!"
    print(greeting)

hello()
---------------------------------------------------------------------------
StdinNotImplementedError                  Traceback (most recent call last)
Cell In[3], line 6
      3     greeting = "Hello " + name + "!"
      4     print(greeting)
----> 6 hello()

Cell In[3], line 2, in hello()
      1 def hello():
----> 2     name = input("Enter your name: ")
      3     greeting = "Hello " + name + "!"
      4     print(greeting)

File /opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/ipykernel/kernelbase.py:1281, in Kernel.raw_input(self, prompt)
   1279 if not self._allow_stdin:
   1280     msg = "raw_input was called, but this frontend does not support input requests."
-> 1281     raise StdinNotImplementedError(msg)
   1282 return self._input_request(
   1283     str(prompt),
   1284     self._parent_ident["shell"],
   1285     self.get_parent("shell"),
   1286     password=False,
   1287 )

StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.
print(greeting)
  • Reason for the exception in the above cell is the scope of the variable greeting is local to the user-defined function hello

greeting = "Hello World!"

def hello():
    name = input("Enter your name: ")
    greeting = "Hello " + name + "!"
    print(greeting)

hello()
print(greeting)
  • This code works now because there are two variables named greeting

    • The variable greeting in the first line of the cell is in the global scope

    • The variable in the function hello is still in the local scope

PI = 3.1415926

def circle_area(diameter):
    def square(x):
        return x*x
    
    area = 0.25*PI*square(diameter)
    return area
  • The variable PI can be used within the circle_area function as it is defined in the global namespace

circle_area(4)
square(2)
  • An exception is raised when the square function is called as the function definition is in the local scope of circle_area function

x = 10  # global x
print('x' in globals())
def f(x):
    x = 20  # local in f
    print(locals())
    print(globals()['x'], x, '\n', sep='\t\t')
    
    def g(x):
        #x = 30  # local in g
        print(locals())
        print(globals()['x'], x, sep='\t\t')
    g(x)
f(x)

Modules#

  • Modules are a set of Python definitions and statements that can be reused across different programs or interactive sessions

    • E.g., os, sys, math, random, timeit

    • Enable reuse of functions, classes etc.

  • Packages are a set of modules

    • E.g., numpy, scipy, matplotlib, …

  • You can create your own user-derfined modules

  • The import statement is used to import the contents of a module

    • Typically, at the beginning of a Python program/script

  • Modules are Python objects that have their own namespace

    • Shared by the functions defined within the module

    • Each of these functions will have its own local namespace when called

  • Python installation comes with certain built-in modules contained in the Python standard library

  • More modules or packages are hosted on the Python package index

    • https://pypi.org/

    • The tool pip is used to manage (install/uninstall/modify) the external modules or packages

      • If both python2 and python3 are installed, then pip3 should be used

    • General installation: type pip install module_name into the terminal

  • Some useful modules from the Python standard library

    • math – mathematical functions

    • cmath – mathematical functions for complex numbers

    • decimal – fast, more accurate floating-point arithmetic

    • random – pseudo-random number generation

    • statistics – statistical functions like mean, median, stdev, variance etc.

    • os – access functions for files and directories, execute OS commands etc.

      • os.path – sub-module for manipulating pathnames, check existence of a file or directory etc.

      • Provides an OS-independent interface and thus, makes the Python code portable

    • sys – a module to interact with the Python interpreter

    • timeit – measure execution time of small bits of python code

  • Importing modules

import module_name as alias
  • as is a Python keyword

  • as alias part is optional and is used when two modules have the same name

  • Examples:

import math           # No alias
import numpy as np    # np is an alias to the numpy module
  • Accessing variables or functions defined within the module’s namespace:

    • When no alias is used: module_name.some_func()

    • When an alias is used: alias.some_func()

  • dir(module_name) gives list of objects within the module

  • help(module_name) gives information about module

    • Also provides breakdown of function and data within module

import math
type(math)
globals()['math']
print(dir(math))   # Using print here just for better formatting, not required
help(math.comb)
math.cos(math.pi/3)
  • Importing everything from the module into the current global namespace

    • from module_name import *

    • When imported this way, the functions and variables defined in the module can be accessed directly (e.g., cos(pi) instead of math.cos(math.pi))

  • Discouraged as this leads to unreadable code and potential conflicts with identifiers

import math
math.sqrt(-1)
  • math module cannot handle complex arithmetic, but cmath does

import cmath
cmath.sqrt(-1)
  • If everything from both modules are loaded simultaneously, only one of the identically named functions (like sqrt) from the module imported later can be used

from cmath import *
from math import *
sqrt(-1)            # From math module
  • Selectively importing parts of modules

    • from module_name import object1, object2, ... , objectN

      • Imports various objects (functions, classes etc.) defined in the module

      • The objects are imported to the global namespace and can be directly accessed

      • Module name is not needed to access the imported objects

      • E.g., from math import cos, sin, tan, pi

    • Each selectively imported object can be given an alias to prevent conflicts with existing identifiers with the as keyword

      • from module_name import object1 as alias1, ... , objectN as aliasN

      • The as keyword can be omitted as needed

        • E.g., from math import cos as math_cos, pi

import math
pi = math.pi
cos = math.cos
cos(pi)
del math
  • The del statement removes objects from the global namespace

from math import cos, pi
cos(pi)
#option 1
import math
cos = math.cos

#option 2
from math import cos

#multi import w/ alias
from math import cos as mathcos, sin, pi

mathcos(pi)

User Defined Modules#

  • To create a custom module named myModule:

    • Save the data, function, and class definitions to a file named myModule.py

    • Add the directory to the module search path defined by sys.path

      • Not needed if saved to the current directory or one of the existing module search paths

    • For a more permanent solution, add the directory to the PYTHONPATH environment variable

import sys
sys.path.append('/home/narasimha/python_notebooks/ME241/')

import myModule
dir(myModule)
print(myModule)
#myMod.x
#myMod.myFun()

import sys
sys.path.append('secret_subfolder')
import myMod2

myMod2.secret()

sys.path.append('/home/jupyter-jsteffens/')
import myMod3

myMod3.super_secret()
  • Example

  • A simple module for element-wise addition and multiplication of lists

# User Module for doing math on lists

repeat = 1

def addLists(list1, list2):
    '''Function for adding lists'''
    if len(list1) != len(list2):
        print("Lists should be of the same size!")
    list3 = []
    for i in range(len(list1)):
        list3.append(list1[i] + list2[i])
    return repeat*list3

def multLists(list1, list2):
    '''Function for multiplying lists'''
    if len(list1) != len(list2):
        print("Lists should be of the same size!")
    list3 = []
    for i in range(len(list1)):
        list3.append(list1[i] * list2[i])
    return repeat*list3
  • listMod.py is located in the current directory, do not need to call sys.path.append()

import listMod
help(listMod)
listMod.repeat = 2

l1 = [1, 2, 3, 4, 5]
l2 = [2, 4, 6, 8, 10]

listMod.multLists(l1, l2)