Just imagine that you are into a large project which involves myriad of methods, functions & classes which has been left by an employee to you. Which means that, an employee who is working on such a project has suddenly left the team or project in-between and you are now put into this project. Now, in order to use any of this method or class, you have to either debug entire application or go to its documentation.
Consider a scenario where you have so many methods and classes that it is very difficult for you to remember the functionality of all of them. Thus, every time when you want to access or use a particular method or class, you have to go to its documentation and read it before using it. This results in wastage of time when you are involved in Rapid Application Development. Also, this affects your continuity and productivity over a long time.
The solution to above given scenarios is SPECIFICATIONS.
- Writing a testing code is much more useful instead debugging an entire application.
- It also forces us to think about which tests are likely to be most illuminating. Thus, writing specifications is easier compared to debugging.
- The text between the triple quotation marks is called a docstring in Python. By convention, Python programmers use docstrings to provide specifications of functions.
- These docstrings can be accessed using the built-in function help. Example is given below.
- A specification of a function defines a contract between the implementer of a function and those who will be writing programs that use the function.
- We will refer to the users of a function as its clients. This contract can be thought of as containing two parts as implemented in the findRoot example:
- Assumptions: These describe conditions that must be met by clients of the function. Typically, they describe constraints on the actual parameters. Almost always, they specify the acceptable set of types for each parameter, and not infrequently some constraints on the value of one or more of the parameters. For example, the first two lines of the docstring of findRoot describe the assumptions that must be satisfied by clients of findRoot.
- Guarantees: These describe conditions that must be met by the function, provided that it has been called in a way that satisfies the assumptions. The last two lines of the docstring of findRoot describe the guarantees that the implementation of the function must meet.
- Just as we have in-built functions like max and abs, we would like to have findRoot as an in-built function and similarly for many other complicated tasks. Functions facilitate this by providing decomposition and abstraction.
- Decomposition creates structure. It allows us to break a problem into modules that are reasonably self-contained, and that may be reused in different settings.
- Abstraction hides detail. It allows us to use a piece of code as if it were a black box—that is, something whose interior details we cannot see, don’t need to see, and shouldn’t even want to see.The essence of abstraction is preserving information that is relevant in a given context, and forgetting information that is irrelevant in that context.
Now, let us use help() to get the specifications of findRoot():
Hope that the usage of specification is very much clear now. We will thus see Global Variables and its usage with example.
- In our above given Fibonacci example, suppose that we want to know the number of recursion calls made. This can be done using a global variable as shown here.
- In each function, the line of code global numFibCalls tells Python that the name numFibCalls should be defined at the outermost scope of the module.
- Modules are files containing Python definitions and statements (ex. name.py)
- A module’s definitions can be imported into other modules by using “import name”
- The module’s name is available as a global variable value
- To access a module’s functions, type “name.function()”
- Modules can contain executable statements along with function definitions
- Each module has its own private symbol table used as the global symbol table by all functions in the module
- Modules can import other modules
- Each module is imported once per interpreter session
- Can import names from a module into the importing module’s symbol table
- from mod import m1, m2 (or *)
We can also execute modules as given below:
- python name.py <arguments>
- Runs code as if it was imported
- Setting _name_ == “_main_” the file can be used as a script and an importable module
Whenever you import a module inside your code, the interpreter needs to search that module. How does this searching take place ?
Consider that we have imported a module or file named name.py
- The interpreter searches for a file named name.py
- Current directory given by variable sys.path
- List of directories specified by PYTHONPATH
- Default path (in UNIX – .:/usr/local/lib/python)
- Script being run should not have the same name as a standard module or an error will occur when the module is imported.
Let’s go for Packages now.
- Packages are “dotted module names” (ex. a.b)
- Submodule b in package a
- Saves authors of multi-module packages from worrying about each other’s module names
- Python searches through sys.path directories for the package subdirectory
- Users of the package can import individual modules from the package
- Ways to import submodules
- import sound.effects.echo
- from sound.effects import echo
- Submodules must be referenced by full name
- An ImportError exception is raised when the package cannot be found.
How to import from module ?
- * does not import all submodules from a package
- Ensures that the package has been imported, only importing the names of the submodules defined in the package
- import sound.effects.echo
- import sound.effects.surround
- from sound.effects import *
- Submodules can refer to each other
- Surround might use echo module
- import echo also loads surround module
- import statement first looks in the containing package before looking in the standard module search path
- Absolute imports refer to submodules of sibling packages
- sound.filters.vocoder uses echo module
- from sound.effects import echo
- Can write explicit relative imports
- from . import echo
- from .. import formats
- from ..filters import equalizer
In order to have more clarity on packages, we need to go through namespaces and types of imports.
Namespaces and Variable Scopes
- A namespace is a mapping of names (identifiers) to objects. The process of adding a name to a namespace consists of binding the identifier to the object (and increasing the reference count to the object by one).
- Namespaces are purely mappings between names and objects, but scope dictates how, or rather where, one can access these names based on the physical location from within your code.
Types of Imports
- Using from-import with packages: e.g. from package.module import *
- Absolute Import
- It is default in Python3.
- As the use of packages becomes more pervasive, there have been more cases of the import of subpackages that end up clashing with (and hiding or shadowing) “real” or standard library modules (actually their names).
- Package modules will hide any equivalently-named standard library module because it will look inside the package first to perform a relative import, thus hiding access to the standard library module.
- Because of this, all imports are now classified as absolute, meaning that names must be packages or modules accessible via the Python path (sys.path or PYTHONPATH).
- The rationale behind this decision is that subpackages can still be accessed via sys.path, i.e., import Phone.Mobile.Analog. Prior to this change, it was legal to have just import Analog from modules inside the Mobile subpackage.
- Relative Import
- Absolute import feature takes away certain privileges of the module writer of packages. With this loss of freedom in import statements, something must be made available to proxy for that loss. This is where a relative import comes in. The relative import feature alters the import syntax slightly to let programmers tell the importer where to find a module in a subpackage.
- The import statements are always absolute, relative imports only apply to from-import statements.
That’s all for this post. In next post, we will see files in Python.
Other Important Topics:
- Python and Other Object-Oriented Programming Languages
- Python 2 vs Python 3
- Introduction to Python Programming
- The Basic Elements of Python Programming
- Branching, Indentation, Looping and Control Structure in Python
- Indexing & Slicing of Strings and Capturing Inputs
- Built-in Data Types and Functions in Python
- Specifications, Global Variables, Modules and Packages in Python
- Working with Files in Python
- Strings in Python
- Lists in Python
- Tuples in Python
- Dictionaries in Python
- Mutable and Immutable Python Objects
- Functions as Objects, map(), filter() and reduce()
- Exception Handling in Python
- Classes and Object Oriented Programming in Python
- Searching Algorithms in Python
- Method Resolution Order (MRO) in Python
- Sorting Algorithms in Python