Working with FUNCTIONS in Python

A function in Python is a named collection of statements which get data (variables or attributes) from arguments (parameters) given on execution .

  print("Hello World")
  def hi(name):
    print("hello", name)

This would allow reuse of a group of statements multiple times with different input of data. This flexibility saves a lot of time and effort. The function is called by simply calling its name followed by () with or without parameters inside these brackets depending on how it was defined. A single function will serve one well-defined action or process. Related functions may be grouped together in a class or a module.

To create a function that writes the Fibonacci series :

  def fib(n):    = write Fibonacci series up to n
    """Print a Fibonacci series up to n."""
    a, b = 0, 1
    while a < n:
      print a,
      a, b = b, a+b
  => Now call the function we just defined:
  fib(2000)

Function as an Object

The function may be assigned to a variable name which will have all its properties. The function is here an object which can be assigned to a variable.

This allows for renaming the function, giving it a different alias and executing it in many different ways. Thus, a function can be passed as a parameter to another function.

rename a function and use its action with the new name :

  def fib(n):    = write Fibonacci series up to n
    """Print a Fibonacci series up to n."""
    a, b = 0, 1
    while a < n:
      print a,
      a, b = b, a+b
  print(fib)
  f = fib
  print(f)
  print f(100)

Return Result

Return followed by an expression or None is a command to exit the function. If there is an expression after Return it passes the result of that expression back to the caller. If there is no expression it returns None. Without Return statement, the function complete successfully and exit with None also returned.

Return can be used before function is completed when a conditional loop is used. Once the condition is met, the function exit, in the same way as 'break' keyword.

  def find_prisoner_with_knife(prisoners):
    for prisoner in prisoners:
        if "knife" in prisoner.items:
            prisoner.move_to_inquisition()
            return = no need to check rest of the prisoners nor raise an alert
    raise_alert()

None returned can be useful outcome if a certain condition is confirmed. In this example, the function checks if the person has a mother and he is human and returns that person's mother name, otherwise return None. In this example Return is used more than once in a conditional statement.

  def get_mother(person):
    if is_human(person):
        return person.mother
    else:
        return None

Default Arguments

Arguments or Parameters are values of data supplied to the function to process. These must have a name and given in the def statement. If not parameters are given, data are included inside the function, but this limit its reuse.

Parameter names are the names of variables to which values will be assigned on execution of the function.

Sometimes, a default value is defined in the function, which will be used if no other value for such variable is given on execution or as a starting value for that variable.

  def count(n=1):
    count += n*20

A more complex example follows. It ask user for data and return True or false, use default arguments for number of attempts, give feedback on errors and check for the right input.

  def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
    while True:
        ok = input(prompt)
        if ok in ('y', 'ye', 'yes'):
            return True
        if ok in ('n', 'no', 'nop', 'nope'):
            return False
        retries = retries - 1
        if retries < 0:
            raise IOError('refusenik user')
        print complaint
  ask_ok('Do you really want to quit?')
  ask_ok('OK to overwrite the file?', 2)
  ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')

Default arguments can be of any type, for example it can be an empty list which accumulates the values passed to the function.

  def f(a, List=[]):
    List.append(a)
    return sum(List)
  f(1)
  f(2)
  f(3)

Keyword Arguments

When calling a function, the caller may use keyword arguments instead of the order given in the function definition. Values for parameters given in the function definition must be provided in the order of these parameters. You can overcome this by giving them in keyword:value argument.

  def my_function(child3, child2, child1):
    print("The youngest child is " + child3)

  my_function(child1 = "Emil", child2 = "Tobias", child3 = "Linus")

You can make one or more argument optional by giving it a default value of None. Positional arguments come before keyword arguments on calling the function.

  def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
    print("-- This parrot wouldn't", action,)
    print("if you put", voltage, "volts through it.")
    print("-- Lovely plumage, the", type)
    print("-- It's", state, "!")
  print(parrot(1000) )      = 1 positional argument
  print(parrot(voltage=1000))   = 1 keyword argument
  print(parrot(voltage=1000000, action='VOOOOOM'))    = 2 keyword arguments
  print(parrot(action='VOOOOOM', voltage=1000000))    = 2 keyword arguments
  print(parrot('a million', 'bereft of life', 'jump'))= 3 positional arguments
  print parrot('a thousand', state='pushing up the daisies') = 1 positional, 1 keyword

Dictionary and Tuples as Keyword Arguments

Instead of manually entering keyword arguments one by one as function parameters, it possible to pack them together into a dictionary or a tuple and send them to the function as one. A dictionary would be unpacked using the double Asterix and tuples or lists by one Asterix. Any parameter missing from the dictionary will use its default value, if any.

  def parrot(voltage, state='a stiff', action='voom'):
    print("-- This parrot wouldn't", action,)
    print("if you put", voltage, "volts through it.",)
    print("E's", state, "!")
  d = {
    "voltage": "four million", 
    "state"  : "bleedin' demised", 
    "action" : "VOOM"}
  parrot(**d)

  range(3, 6)             = normal call with separate arguments =>[3, 4, 5]
  args = [3, 6]
  range(*args)

Lambda as unnamed function

Lambda is anonymous veru short function which has its parameters and can be used anywhere even inside a named function. It always return something, so there is no return statement here.

  def make_incrementor(n):
    return lambda x: x + n
  f = make_incrementor(42)
  print f(0)
  print f(1)

Lambda is particularly useful when used with built-in functions such as map, filter and reduce.

  list = [1,2,3,4,5]
  squaredList = map(lambda x: x*x, list)
  print(squaredList)

  list = [1,2,3,4,5,6,7,8,9,10]
  newList = filter(lambda x: x % 2 == 0, list)
  print(newList)

  list = [1,2,3,4,5]
  sm = reduce(lambda x,y: x+y,  list)
  print(sm)

Lambda may be passed as an argument to another built-in function such as sort function

  pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
  pairs.sort(key=lambda pair: pair[1])
  print(pairs)

Docstring

It is a matter of good style to use a docstring to describe purpose of a function. Such description would make it more readable and for someone who review the code to know what the function is about. They are similar to comments, but comments are short and used for a single expressions or a statement. Comments are useful to explain tasks to be done, bugs and the general purpose of the module. Docstrings can be part of the documentation of the module or package.You can access documentation of a module by calling "module.doc" or help(module).

You can use a single or double quote for a single line docstring, but use triple quotes for multiline docstring.

  def my_function():

    """Short, concise summary of the object’s purpose.
     (Starts with Capital letter and ends with period)
     (separated from description by blank line)
     More paragraphs describing the object’s calling conventions, 
     its side effects..etc.
    """
    pass
  print(my_function.__doc__)