Names and Namespaces
Python names are how Python is able to identify functions, variables and modules. Names are made up of letters, underscores and digits, but may only start with letters and underscores. We'll see later when we cover classes that those that start with two underscores have a special meaning.
Namespaces
Within different context and scopes, names can have entirely different meanings. The space in which a name is mapped to an object thing is called the namespace.
Local and Global namespaces
Local namespaces are created when a function is called, and deleted when the function returns a value or raises an exception that is not handled within the function. Unless a variable is declared with the global
keyword, it is defaulted to the local namespace.
Within a global namespace, a name has the same meaning everywhere. In Python, namespaces are implemented in the dictionary datatype, which we'll see later.
Overwriting names
Be cautious when creating a name to make sure it's not already taken. If you import or use a name that was already in use, the previous name will be overwritten. This will cause unexpected errors and bugs all over your program so take caution!
Assigning names
Assigning names is easy. Simply use the =
operator, with the name on the left-hand side.
>>> sample = "test"
>>> a = b = c = 5 # Assigns all three variables with the number 5.
Augmented assignments
You can easily modify an already existing variable, with an augmented assignment operator.
>>> i = 5
>>> i += 5 # Same as i = i + 5
Other augmented assignments include +=
(addition), -=
(subtraction), *=
(multiplication), /=
(division), //=
(flooring), %=
(modulo, remainder), **=
(exponent).
Functions
A function is a small chunk of reusable code that performs some task. Utilizing functions helps us easily debug and maintain our code. Functions also avoid code duplication and keeps our code looking clean. As a programmer, you should try to separate each small task within individual functions for better readability and good practice.
We've already looked at method and function calls earlier on. Let's now learn how to define our own functions.
Defining functions
To define a function, use the def
keyword (short for definition), followed by your function name. Within the following set of parentheses, specify your formal parameters.
The following function doubles a value and adds one to it.
def doubleAndAdd(n):
return 2*n+1
Python requires that each line that is part of the function to be indented inward. If we don't indent each line within our function, then we get an error message.
def doubleAndAddOne(n):
return n+1
File "<stdin>", line 2
return 2*n+1;
^
IndentationError: expected an indented block
Return
The return
keyword is used to return a value to its caller. If no value is specified after the return
keyword, then None
is returned.
Doing nothing
While developing programs, you may find yourself defining functions that are yet to be implemented. To avoid Python erroring out a function with no statements, use the keyword pass
as a filler until you figure out what to write.
def doubleAndAddOne(n):
# TODO: implement doubleAndAddOne formula
pass
Docstring
As programmers, it's good practice to communicate to others about what our functions do. The opening line of the function is known as the docstring. It is a literal string value that describes the function. To declare it, use three double-quotation marks, right after the function definition.
def doubleAndAddOne(n):
"""This function returns an input value plus one."""
return 2*n+1
Now when the user prompts the help function, he or she can read the brief description.
>>> help(doubleAndAddOne)
Help on function doubleAndAddOne in module __main__:
doubleAndAddOne(n)
This function returns an input value plus one.
(END)
Docstring Conventions
- The first line should be a short, concise summary of object or function's purpose.
- First line should start with a capital letter and end with a period.
- We do not need to declare what type it is since the user can obtain that be other means.
- If there are more than one line, then the second line should be blank.
Symbol table
With every function execution, a new symbol table is created. The symbol table stores all the local variables that are initialized from within our function. Thus, when a variable is called, Python looks within the first symbol table on our stack. If nothing is found there, then it moves up to the next functions, then to the global symbol table and finally the built-in names. If still nothing is found, an error results.
The only way a global variable can be assigned is through the global
statement.
Default and optional parameters
Sometimes you want to make a function parameter value optional. In such cases, the parameters will take on a default value if the variable is undefined.
def doubleAndAddOne(n, double=False):
"""This function returns an input value plus one."""
return 2*n+1 if double else n+1
>>> doubleAndAddOne(2)
# double is automatically set to False
3
To specify a variable, specify the variable and the value you want to give it.
>>> doubleAndAddOne(2, double=True)
5
Arbitrary arguments
You may want to specify an arbitrary number of arguments into variadic arguments. These will take in all other parameters and store it into a tuple. Precede this special argument with an asterisk (*
).
def arb_arguments(*args, param = 'default'):
print("arbitrary argument list:")
for arg in args:
print(arg)
print("param = " + param)
return
>>> arb_arguments(1,2,3,4)
arbitrary argument list:
1
2
3
4
param = default
>>> arb_arguments('hello', 'hi', 'whatsup', param = 'custom')
arbitrary argument list:
hello
hi
whatsup
param = custom
Assertions
Assertions can be used to check certain values from within a Python program. For example, we can write the following in our code to ensure we have a positive value.
def doubleAndAddOne(n):
""" This function returns an input value plus one. """
assert n > 0
return 2*n+1
Now when we specify a negative value, we can see that our assertion fails.
>>> doubleAndAddOne(-2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, indoubleAndAddOne
AssertionError
Custom assertion error messages
To create your own customer assertion error message, input a second parameter. This will be triggered if our assertion fails.
def doubleAndAddOne(n):
""" This function returns an input value plus one. """
assert n > 0, "Please enter a positive value."
return 2*n+1
Now we can see our own custom error message returned if a negative value is put in.
>>> help(doubleAndAddOne)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, indoubleAndAddOne
AssertionError: Please enter a positive value.
Lambda Expressions
To create an anonymous function, use the lambda keyword. In this function, we'll first assign x
, then let any future functions assign the other variables.
>>> def multiply(x):
... return lambda a, b: x * (a + b)
>>> f = multiple(2)
>>> f(3,4) # 2 * (3+4)
14
Modules
Assume that you were working in a session and implemented a handful of functions. When you close that session, all those functions will be lost. For the next session, you would have to tediously rewrite and import each function.
A better approach to solving this problem is to import a python script that contains all the relevant functions. Such a script is known as a module, which can be imported with a single command.
Additionally, you could download a complete set of functions that are pre-written from the web. Such extensibility is what makes Python such an attractive language for data scientists and application programmers, who are able to use these packages to ease their workflow.
Creating your own module
To create your own module, create a plain text file with the .py extension.
# module hi.py
def HelloWorld():
print("Hello World!")
Importing Modules
Now if we are in the same folder as hi.py, and we initialize Python, we may import
our module.
>>> hi.HelloWorld()
Hello World!
Accessing functions directly
Note that the function names are stored within the local symbol table, so you have to precede each function call with hi
. If you'd like to access the functions directly, you may do so:
>>> from sample import HelloWorld
>>> HelloWorld()
Hello World!
Importing all functions
Importing all modules is sometimes considered bad practice, as you may be overwriting names that already exist. However, if you are sure no important names are overwritten, and want to make your code looking cleaner, you can do so using from sample import *
. This effectively imports all functions from the sample.py module.
Importing a single function
In some cases you'll want to import just one function instead of the entire module. We may do so with the following syntax.
>>> from os import getcwd
>>> getcwd()
'/Users/JohnDoePC/Dropbox'
You may also want to import using a different name (alias). To do so, use the from
and as
keywords.
>>> from os import getcwd as pwd
>>> pwd()
'/Users/JohnDoePC/Dropbox'
Sample modules
Two of the most commonly used modules are the OS and Random modules. Let's take a quick look at them and how they can be used.
The OS Module
The os
module provides an interface to the current operating system.
>>> import os
>>> os.getcwd()
'/Users/JohnDoePC/Dropbox'
>>> os.getlogin()
JohnDoePC
The Random Module
Another important module is one that allows you to generate random numbers. This is known as the random
module.
>>> import random
>>> random.randint(2,4)
# Generates a random number between 2 and 4 (inclusive)
2