Chapter 4 Functions

4.7 Library Modules

The Python modules that have been introduced in this book are used as libraries, which are assemblies of code that provide specific functionalities for reuse. Most Python Library modules are nothing but normal Python programs, typically stored in a specific library path in the file system.

Python automatically searches for the locations when a library module is imported, where the folder names depend on the Python version. Note that the math module is an exception: it cannot be found under the library folder locations above. This is because math is a very commonly used module, and is built in the main Python program itself. Other commonly used modules, including cmath, are also built-in.

Making Use of Libraries

Before writing one’s own code for a specific functionality, it is wise to check whether there is an existing library module that can be used directly. Python provides many powerful functionalities through modules distributed along with the Python program itself, and the descriptions of all the modules can be found in the Python documentation. To look for third-party modules with given functionalities, online search engines are a useful resource. For example, searching for ‘Python scientific computation’ can lead to the website of SciPy.

The Mechanism of Module Importation

The import statement creates an identifier that has the same name as the module. For example, by using ‘import random’, ‘random.py’ is imported into the memory, and associated with the name ‘random’ in the global binding table. To avoid name clashes and for the convenience of reference, a module can also be given a different name when imported into the binding table. This can be achieved by using a variant of the import statement, with the syntax ‘import x as y’, where x is the name of the module in the file system and y is the desired name in the binding table.

>>> import math as m
>>> m.e
2.718281828459045
>>> m.sqrt(25.0)
>>> 5.0
>>> math.e
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'math' is not defined

In the example above, the math module is loaded into the binding table and associated with the identifier m. As a result, by using “m.”, the corresponding binding table of the math module can be accessed. However, the identifier “math” does not exist in the global binding table this time.

Since both the assignment statement and the import statement changes the binding table, they can override the value of an identifier. For example,

>>> x=1
>>> import math as x
>>> x+1 # not a number
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'module' and 'int'
>>> x.pi # but a module
3.141592653589793

In the example above, x is initially bound to a number, but then rebound to a module. The original binding is overridden.

Importing Specific Identifiers

Sometimes only a small number of functions or variables needs to be imported from a module. In such cases, the use of a module name in reference to the imported functions or variables may be unnecessary. For example, suppose that only pi and sin are needed by a program. In this case, importing the two identifiers directly into the global binding table can be more convenient than importing the math module, because in this way pi and sin can be referred to directly, instead of using math.pi and math.sin.

Python provides a variation of the import statement for such imports. The syntax is:

from <module name > import <identifiers >

where <identifiers> is a comma-separated list of specific identifiers, or a wildcard * that represents all the identifiers in the module.

In the following example, the variable pi and the function sin are imported from the math module directly.

>>> from math import pi , sin
>>> sin(pi)
1.2246467991473532e-16
>>> math.pi
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'math' is not defined

In the example above, the module name math cannot be found in the global binding table, but the identifiers pi and sin are present. The mechanism for the fromimport … statement is illustrated in Fig. 4.3. It consists of two steps: (1) execute the module to be imported; (2) add the specified identifiers to the global binding tables. The process is the same as the import … statement in the first step, but differs in the second step. As shown in Fig. 4.3, the resulting global binding table contains the identifiers pi and sin, but not the identifier math. The binding table of the math module is not referred to by any identifier in this case, and will be removed by the garbage collector, together with the objects in the module other than pi and sin.

Fig. 4.3 Memory structure of from ... import ...

Fig. 4.3 Memory structure of from … import …

The following example shows the importing of all identifiers from the math module.

*dir* and the *__builtins__* binding table

Python allows the listing of all entries in the global binding table by using the dir function.

>>>dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
>>> x=1
>>> y=2
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'x', 'y']

In the example above, the first dir function call gives default entries when IDLE (or a Python program) starts. Details about these entries will be introduced later. When the two assignment statements x = 1 and y = 2 have been executed, two more entries, x and y are added to the global binding table, as shown by the second call to the dir function.

The dir function can also take an input argument, which is an object that has a binding table, and lists out all the entries in the binding table. As introduced earlier, module objects are associated with binding tables. As a result, one can list out all the identifiers defined in a module.

>>>import math
>>>dir(math)
['__doc__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp',   'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']

Note that the input argument to the dir function call must be available in the current global binding table. Otherwise, an error will be given.

>>>dir(random) # random has not been imported
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'random' is not defined

In addition to modules, other objects can also be associated with binding tables, and they will be introduced later.

It can be noticed that the global binding table does not contain built-in functions such as id, int, len and dir. The reason that they can be referred to without causing an error is that Python organizes all built-in functions in a special module, __builtins__, which is implicit in the system. When an identifier cannot be found in the global binding table, the __builtins__ binding table will be searched. In fact, the __builtins__ in the global binding table by default, as shown earlier.

>>> dir(__builtins__)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']

There are many built-in functions, such as round and len; many more will be introduced in this book. By convention, Python names implicit or special objects by enclosing their name in double underscores (__).

Check your understanding

© Copyright 2024 GS Ng.

Next Section - 5.1 The Boolean Type