# Recursion – Algorithm Study

Recursion is a technique by which a function calls itself until a condition is met.

## Introduction

Loops or repetitive execution based on certain conditions are inevitable in programs. Usual loops include `if`, `while` and `for` loops. `Recursion` is an entirely different way to deal with such situations, and in many cases, easier.

`Recursion` is a when a function calls itself in each iteration till a condition is met. Ideally, the data set in each iteration gets smaller until it reach the required condition, after which the recursive function exists.

A typical example of recursion is a `factorial` function.

## How does Recursion work?

A `recursive` function ideally contains a `Base` case and a `Recursive` case.

`Recursive` case is when the function calls itself, until the `Base` case is met. Each level of iteration in the `Recursive` case moves the control to the next level.

Once a specific level finishes execution, the control is passed back to the previous level of execution. A `Recursive` function can go several layers deep until the `Base` condition is met. In short, a `Recursive` case is a loop in which the function calls itself.

The `Base` case is required so that the function doesn’t continue running in the `Recursive` loop forever. Once the `Base` case is met, the control moves out of the `Recursive` case, executes the conditions in the `Base` case (if any), and exits.

As mentioned in the `Introduction`, a factorial function can be seen as an example of recursion.

### NOTE:

The `Base` case for a factorial function is when `n == 1`

Consider `n!`:

`n!` can be written as:

n x (n – 1) x (n – 2) x (n – 3) x …. x 1

`n!` can also be represented as:

``` n!       = n * (n - 1)! ---> [Step 1]
(n - 1)! = (n - 1) * (n - 2)! ---> [Step 2]
(n - 2)! = (n - 2) * (n - 3)! ---> [Step 3]
.
..
...
(n - (n - 1)) = 1 ---> [Base case]
```

Each level/step is a product of a value and all the levels below it. Hence, `Step 1` will end up moving to `Step 2` to get the factorial of elements below it, then to `Step 3` and so on.

ie.. the control of execution move as:

[Step 1] -> [Step 2] -> [Step 3] -> ….. [Step n]

In a much easier-to-grasp example, a `5!` would be:

```5! = 5 * 4! ---> [Step 1]
4! = 4 * 3! ---> [Step 2]
3! = 3 * 2! ---> [Step 3]
2! = 2 * 1! ---> [Step 4]
1! = 1      ---> [Step 5] / [Base case]
```

The order of execution will be :

[Step 1] -> [Step 2] -> [Step 3] -> [Step 4] -> [Step 5]

As we know, in `Recursion`, each layer pause itself and pass the control to the next level. Once it reach the end or the `Base` case, it returns the result back to the previous level one by one until it reaches where it started off.

In this example, once the control of execution reaches `Step 5 / Base case` ,  the control is returned back to its previous level `Step 4` . This level returns the result back to `Step 3` which completes its execution and returns to `Step 2` , so on and so forth until it reach  `Step 1` .

The return control flow would be as:

[Base case / Step 5] -> [Step 4] -> [Step 3] -> [Step 2] -> [Step 1] -> Result.

This can be summed up using an awesome pictorial representation, from the book `Grokking Algorithms` by Adit. Please check out the `References` section for the link for more information about this awesome book.

Figure 1: Recursion, Recursive case and Base case (Copyright Manning Publications, drawn by adit.io)

## Code

### Example 1:

• A `factorial` function in a `while` loop
```def fact(n):
factorial = 1
while n > 1:
factorial = factorial * n
n = n - 1
return factorial

print("Factorial of {0} is {1}".format(10, fact(10)))
print("Factorial of {0} is {1}".format(20, fact(20)))
```
• The same function above, in a `recursive` loop
```def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n - 1)

print("Factorial of {0} is {1}".format(10, factorial(10)))
print("Factorial of {0} is {1}".format(20, factorial(20)))
```

### Example 2:

• A function to sum numbers in a normal `for` loop.
```def my_sum(my_list):
num = 0
for i in my_list:
num += i
return num

print(my_sum([10, 23, 14, 12, 11, 94, 20]))
```
• The same function to add numbers, in a `recursive` loop
```def my_sum(my_list):
if my_list == []:
return 0
else:
return my_list[0] + my_sum(my_list[1:])

print(my_sum([10, 23, 14, 12, 11, 94, 20]))
```

## Code explanation

Both `Example 1` and `Example 2` are represented as an iterative function as well as a recursive function.

The iterative function calls the `next()` function on the iterator `sum.__iter__()` magic method iterate over the entire data set. The recursive function calls itself to reach a base case and return the result.

## Observations:

While a recursive function does not necessarily give you an edge on performance, it is much easier to understand and the code is cleaner.

Recursion has a disadvantage though, for large data sets. Each loop is put on a call stack until it reaches a `Base` case. Once the `Base` case is met, the call stack is rewound back to reach where it started, executing each of the previous levels on the way. The examples above showed a `sum` function and a `factorial` function. In large data sets, this can lead to a large call stack which in turns take a lot of memory.

# Python, Objects, and some more..

Everything in Python is an object, what does that mean? This post tries to discuss some very basic concepts.

What does the following assignment do?

```
a = 1
```

Of course, anyone dabbled in code knows this. The statement above creates a container `a` and stores the value `1` in it.

But it seem that’s not exactly what’s happening, at least from Python’s view-point.

When `a = 1` is entered or executed by the python interpreter, the following happens in the backend, seemingly unknown to the user.

• The Python interpreter evaluates the literal `1` and tries to understand what data type can be assigned for it.
• There are several in-built data types such as `str`, `float`, `bool`, `list`, `dict`, `set` etc..
• Builtin types are classes implemented in the python core.
• For a full list of types and explanation, read the python help at `python`-> `help()`-> `topics` -> `TYPES`
• Read the help sections for builtin types, eg.. `help(int)`, `help(list)` etc..
• The interpreter finds the appropriate builtin type for the literal. Since the literal `1` fits the type `int`, the interpreter creates an instance from `class int()` in memory.
• This instance is called an `object` since it’s just a `blob` with some metadata.
• This object has a memory address, a value, a name in one or more namespace, some metadata etc..
• `type(a)` helps in understanding the instance type.
• In short, an assignment statement simply creates an instance in memory from a pre-defined class.
• The interpreter reads the LHS (Left hand side) of the statement `a = 1`, and creates the name `a` in the current namespace.
• The name in the namespace is a reference to the object in memory.
• Through this reference, we can access the data portion as well as the attributes of that object.
• A single object can have multiple names (references).
• The name `a` created in the current namespace is linked to the corresponding object in memory.

When a name that’s already defined is entered at the python prompt, the interpreter reads the namespace, finds the name (reference), goes to the memory location it’s referring to, and pull the value of the object, and prints it on-screen.

#### Every object has the following features:

• A single value, available in its data section.
```In [1]: a = 1

In [2]: a
Out[2]: 1
```
• A single type, since the object is an instance of a pre-defined type class such as `int` , `float` etc..
```In [3]: type(a)
Out[3]: int
```
• Attributes either inherited from the parent type class or defined by the user.
```In [10]: dir(a)
Out[10]:
['__abs__',
'__and__',
'__bool__',
'__ceil__',
'__class__',
'__delattr__',
'__dir__',
'__divmod__',
'__doc__',
'__eq__',
'__float__',
...[content omitted]
'__setattr__',
'__sizeof__',
'__str__',
'__sub__',
'__subclasshook__',
'__truediv__',
'__trunc__',
'__xor__',
'bit_length',
'conjugate',
'denominator',
'from_bytes',
'imag',
'numerator',
'real',
'to_bytes']
```
• One or more base classes. All new-stlye classes in Python ultimately inherits from the `object` class.
```In [4]: type(a)
Out[4]: int

In [5]: int.mro()
Out[5]: [int, object]
```

NOTE: `a` is an instance of the `int` class, and `int` inturn inherits from the `object` class. Read more on Method Resolution Order.

• A unique ID representing the object.
```In [6]: id(a)
Out[6]: 140090033476640
```
• Zero, One, or more names.
• Use `dir()` to check the current namespace.
• Use `dir(<object-name>)` to refer the indirect namespace.

Several other builtins are available in the default namespace without defining them specifically, possible due to the inclusion of the `builtin` module available under the reference `__builtin__` in the current namespace.

For a full list of the pre-defined variables, refer `dir(__builtins__)``help(__builtin__)` or `help(builtins)` after an `import builtins`.

#### A few questions and observations:

Q1. How can an assignment have zero names in the namespace?

Ans: An assignment such as `a = 1` creates an object in memory and creates a corresponding name (`a` in our case) in the namespace. `a` acts as a reference to the object in memory.

But, simply entering `1` at the python prompt creates an object in memory which is an instance of a type class, without creating the reference in the namespace.

Objects which don’t have a reference from the current namespace are usually garbage-collected due to lack of references. Hence, an object which doesn’t have a reference (a name), or had multiple references (more than one names) but had them deleted (for example, `del()` gets garbage-collected by python.

If the assignment `1` happens to be at a python prompt, it echoes the literal back after creating the object and reference since the prompt is essentially a `REPL` (Read Eval Print loop)

Q2. Can an object have more than one name references?

Ans: It’s perfectly fine to have more than one reference to a single object. The example below should explain things very well.

```In [1]: a = 5000

In [2]: id(a)
Out[2]: 140441367080400

In [3]: b = a

In [4]: b
Out[4]: 5000

In [5]: id(b)
Out[5]: 140441367080400

In [6]: c = 5000

In [7]: id(c)
Out[7]: 140441367080432

In [8]: a is b
Out[8]: True

In [9]: a == b
Out[9]: True

In [10]: a is c
Out[10]: False

In [11]: a == c
Out[11]: True
```

The example shown above creates an object with value `5000` and assign it a name `a` in the current namespace. We checked the identifier of the object using `id(a)` and found out it to be `140441367080400`.

As the next step, we created another name in the namespace, ie.. `b` which takes in whatever `a` points to. Hence, `b` would default to `5000` and it will have the same identifier as `a`.

This shows that an object in memory can have multiple references in a namespace.

Another object of value `5000` is created with a name `c` , but we can see that the identifier differs from what `id(a)` and `id(b)` is. This shows that `c` points to an entirely different object in memory.

To test if `a` is exactly the same object as `b`, use the keyword `is`. Meanwhile, if you want to test if two objects contain the same value, use the equality `==` symbol.

# Abstract Base Classes/Methods – Object Oriented Programming

Abstract classes, in short, are classes that are supposed to be inherited or subclassed, rather than instantiated.

Through Abstract Classes, we can enforce a blueprint on the subclasses that inherit the Abstract Class. This means that Abstract classes can be used to define a set of methods that must be implemented by it subclasses.

Abstract classes are used when working on large projects where classes have to be inherited, and need to strictly follow certain blueprints.

Python supports Abstract Classes via the module `abc` from version 2.6. Using the `abc` module, its pretty straight forward to implement an Abstract Class.

Example 0:

```import abc

class My_ABC_Class(object):
__metaclass__ = abc.ABCMeta

@abc.abstractmethod
def set_val(self, val):
return

@abc.abstractmethod
def get_val(self):
return

# Abstract Base Class defined above ^^^

# Custom class inheriting from the above Abstract Base Class, below

class MyClass(My_ABC_Class):

def set_val(self, input):
self.val = input

def get_val(self):
print("\nCalling the get_val() method")
print("I'm part of the Abstract Methods defined in My_ABC_Class()")
return self.val

def hello(self):
print("\nCalling the hello() method")
print("I'm *not* part of the Abstract Methods defined in My_ABC_Class()")

my_class = MyClass()

my_class.set_val(10)
print(my_class.get_val())
my_class.hello()
```

In the code above, `set_val()` and `get_val()` are both abstract methods defined in the Abstract Class `My_ABC_Class()`. Hence it should be implemented in the child class inheriting from `My_ABC_Class()`.

In the child class `MyClass()` , we have to strictly define the abstract classes defined in the Parent class. But the child class is free to implement other methods of their own. The `hello()` method is one such.

This will print :

```# python abstractclasses-1.py

Calling the get_val() method
I'm part of the Abstract Methods defined in My_ABC_Class()
10

Calling the hello() method
I'm *not* part of the Abstract Methods defined in My_ABC_Class()
```

The code gets executed properly even if the  `hello()` method is not an abstract method.

Let’s check what happens if we don’t implement a method marked as an abstract method, in the child class.

Example 1:

```import abc

class My_ABC_Class(object):
__metaclass__ = abc.ABCMeta

@abc.abstractmethod
def set_val(self, val):
return

@abc.abstractmethod
def get_val(self):
return

# Abstract Base Class defined above ^^^

# Custom class inheriting from the above Abstract Base Class, below

class MyClass(My_ABC_Class):

def set_val(self, input):
self.val = input

def hello(self):
print("\nCalling the hello() method")
print("I'm *not* part of the Abstract Methods defined in My_ABC_Class()")

my_class = MyClass()

my_class.set_val(10)
print(my_class.get_val())
my_class.hello()
```

Example 1 is the same as Example 0 except we don’t have the `get_val()` method defined in the child class.

This means that we’re breaking the rule of abstraction. Let’s see what happens:

```# python abstractclasses-2.py
Traceback (most recent call last):
File "abstractclasses-2.py", line 50, in
my_class = MyClass()
TypeError: Can't instantiate abstract class MyClass with abstract methods get_val
```

The traceback clearly states that the child class `MyClass()` cannot be instantiated since it does not implement the Abstract methods defined in it’s Parent class.

We mentioned that an Abstract class is supposed to be inherited rather than instantiated. What happens if we try instantiating an Abstract class?

Let’s use the same example, this time we’re instantiating the Abstract class though.

Example 2:

```import abc

class My_ABC_Class(object):
__metaclass__ = abc.ABCMeta

@abc.abstractmethod
def set_val(self, val):
return

@abc.abstractmethod
def get_val(self):
return

# Abstract Base Class defined above ^^^

# Custom class inheriting from the above Abstract Base Class, below

class MyClass(My_ABC_Class):

def set_val(self, input):
self.val = input

def hello(self):
print("\nCalling the hello() method")
print("I'm *not* part of the Abstract Methods defined in My_ABC_Class()")

my_class = My_ABC_Class()    # <- Instantiating the Abstract Class

my_class.set_val(10)
print(my_class.get_val())
my_class.hello()
```

What does this output?

```# python abstractclasses-3.py
Traceback (most recent call last):
File "abstractclasses-3.py", line 54, in <module>
my_class = My_ABC_Class()
TypeError: Can't instantiate abstract class My_ABC_Class with abstract methods get_val, set_val
```

As expected, the Python interpreter says that it can’t instantiate the abstract class My_ABC_Class.

### Takeaway:

1. An Abstract Class is supposed to be inherited, not instantiated.
2. The Abstraction nomenclature is applied on the methods within a Class.
3. The abstraction is enforced on methods which are marked with the decorator `@abstractmethod` or `@abc.abstractmethod`, depending on how you imported the module, `from abc import abstractmethod` or `import abc`.
4. It is not mandatory to have all the methods defined as abstract methods, in an Abstract Class.
5. Subclasses/Child classes are enforced to define the methods which are marked with `@abstractmethod` in the Parent class.
6. Subclasses are free to create methods of their own, other than the abstract methods enforced by the Parent class.

# Magic methods and Syntactic sugar in Python

### Magic methods

Magic methods are special methods which can be defined (or already designed and available) to act on objects.

Magic methods start and end with underscores `"__"`, and are not implicitly called by the user even though they can be. Most magic methods are used as syntactic sugar by binding it to more clear/easy_to_understand keywords.

Python is mostly objects and method calls done on objects. Many available functions in Python are actually tied to magic methodsLet’s checkout a few examples.

Example 0:

```In [1]: my_var = "Hello!"

In [2]: print(my_var)
Hello!

In [3]: my_var.__repr__()
Out[3]: "'Hello!'"
```

As we can see, the `__repr__()` magic method can be called to print the object, ie.. it is bound to the `print()` keyword.

This is true for many other builtin keywords/operators as well.

Example 1:

```In [22]: my_var = "Hello, "
In [23]: my_var1 = "How are you?"

In [24]: my_var + my_var1
Out[24]: 'Hello, How are you?'

Out[25]: 'Hello, How are you?'
```

Here, Python interprets the `+` sign as a mapping to the magic method `__add__()`, and calls it on the L-value (Left hand object value) `my_var`, with the R-value (Right hand object value) as the argument.

When a builtin function is called on an object, in many cases it is mapped to the magic method.

Example 2:

```In [69]: my_list_1 = ['a', 'b', 'c', 'd']

In [70]: 'a' in my_list_1
Out[70]: True

In [71]: my_list_1.__contains__("a")
Out[71]: True
```

The `in` builtin is mapped to the `__contains__()`method.

The methods available for an object should mostly be dependent on the type of the object.

Example 3:

```In [59]: my_num = 1

In [60]: type(my_num)
Out[60]: int

In [61]: my_num.__doc__
Out[61]: Out[61]: "int(x=0) -> int or long\nint(x, base=10) -> int or long\n\nConvert a number or string to an integer, or return 0 if no arguments\nare given. ....>>>

In [62]: help(my_num)
class int(object)
| int(x=0) -> int or long
| int(x, base=10) -> int or long
|
| Convert a number or string to an integer, or return 0 if no arguments
| are given. If x is floating point, the conversion truncates towards zero.
| If x is outside the integer range, the function returns a long instead.

```

From the tests above, we can understand that the `help()` function is actually mapped to the `object.__doc__` magic method. It’s the same doc string that __doc__ and help() uses.

NOTE: Due to the syntax conversion (`+` to `__add__(),and other conversions`), operators like `+` , `in`, etc.. are also called Syntactic sugar.

### What is Syntactic sugar?

According to Wikipedia, Syntact sugar is:

In computer science, syntactic sugar is syntax within a programming language that is designed to make things easier to read or to express. It makes the language “sweeter” for human use: things can be expressed more clearly, more concisely, or in an alternative style that some may prefer.

Hence, magic methods can be said to be Syntactic sugar. But it’s not just magic methods that are mapped to syntactic sugar methods, but higher order features such as Decorators are as well.

Example 4:

```def my_decorator(my_function):
def inner_decorator():
print("This happened before!")
my_function()
print("This happens after ")
print("This happened at the end!")
return inner_decorator

def my_decorated():
print("This happened!")

var = my_decorator(my_decorated)

if __name__ == '__main__':
var()
```

The example above borrows from one of the examples in the post on Decorators.

Here, `my_decorator()` is a decorator and is used to decorate `my_decorated()`. But rather than calling the decorator function `my_decorator()` with the argument `my_decorated()`, the above code can be syntactically sugar-coated as below:

```def my_decorator(my_function):
def inner_decorator():
print("This happened before!")
my_function()
print("This happens after ")
print("This happened at the end!")
return inner_decorator

@my_decorator
def my_decorated():
print("This happened!")

if __name__ == '__main__':
my_decorated()
```

Observing both code snippets, the decorator is syntactically sugar coated and called as:

@my_decorator

instead of instantiating the decorator with the function to be decorated as an argument, ie..

var = my_decorator(my_decorated)

### A few syntax resolution methods:

1. ‘name’ in my_list       ->      my_list.__contains__(‘name’)
2. len(my_list)                  ->      my_list.__len__()
3. print(my_list)              ->      my_list.__repr__()
4. my_list == “value”     ->      my_list.__eq__(“value”)
5. my_list[5]                      ->      my_list.__getitem__(5)
6. my_list[5:10]                 ->     my_list.__getslice__(5, 10)

1. A Guide to Python’s Magic Methods, by Rafe Kettler
2. Special method names, The Official Python 3 documentation

# Decorators – Object Oriented Programming

Decorators are wrapper functions (or classes) that wrap and modify another function (or class), and change it’s behavior as required. Decorators help to modify your code without actually modifying the working function/class itself.

There are several inbuilt Decorators in Python, such as `@classmethod` and `@staticmethod`. Examples on these are due for another post.

Decorators are called to act upon a function or class, by mentioning the Decorator name just above the function/class.

Decorators are written such as it returns a function, rather than output something.

Example 0:

```@my_decorator
def my_func():
print("Hello")

my_func()
```

In the above code snippet, when `my_func()` is called, the python interpreter calls the decorator function `my_decorator`, executes it, and then passes the result to `my_func()`.

The example above doesn’t do anything worth, but the following example should help to get a better idea.

NOTE: The examples below are taken from the excellent talks done by Jillian Munson (in PyGotham 2014) and Mike Burns for ThoughtBot. The URLs are at [1] and [2]. All credit goes to them.

Example 1:

```def my_decorator(my_function):
def inner_decorator():
print("This happened before!")
my_function()
print("This happens after ")
print("This happened at the end!")
return inner_decorator

@my_decorator
def my_decorated():
print("This happened!")

if __name__ == '__main__':
my_decorated()
```

### Components:

1. A function named `my_decorated()`.
2. A decorator function named `my_decorator()`.
3. The decorator function `my_decorator()` has a function within itself named `inner_decorator()`.
4. The decorator function `my_decorator()`, returns the inner function `inner_decorator()`.
1. Every function should return a value, if not it defaults to `None`.
2. `my_decorator()` decorator should return the `inner_decorator()` inner function, else the decorator cannot be used with the `my_decorated()` function.
3. To understand this, test with ‘return None’ for the decorator function `my_decorator()`.
5. The inner function `inner_decorator()` is the one that actually decorates (modifies) the function `my_decorated()`.
6. The decorator function is called on the function `my_decorated()` using the format `@my_decorator`.
7. The decorator function takes an argument, which can be named whatever the developer decides. When the decorator function is executed, the argument is replaced with the function name on which the decorator is executed. In our case, it would be `my_decorated()`

### How does the code work?

1. The function `my_decorated()` is called.
2. The interpreter sees that the decorator `@my_decorator` is called wrt this function.
3. The interpreter searches for a function named `my_decorator()`and executes it.
4. Since the decorator function returns the inner function `inner_decorator()`, the python interpreter executes the inner function.
5. It goes through each steps, reaches `my_function()` , and gets it executed.
6. Once that function is executed, it goes back and continues with the execution of the decorator `my_decorator()`.

### Output:

```# python decorators-1.py
This happened before! # Called from the decorator
This happened! # Called from the function
This happens after # Called from the decorator
This happened at the end! # Called from the decorator
```

Example 2:

```def double(my_func):
def inner_func(a, b):
return 2 * my_func(a, b)
return inner_func

@double
return a + b

@double
def subtractor(a, b):
return a - b

print(subtractor(6, 1))
```

### Components:

1. Two functions named `adder()` and `subtractor()`.
2. A decorator function named `double()`.
3. The decorator has an inner function named `inner_func()` which does the actual intended work.
4. The decorator returns the value of the inner function `inner_func()`
5. Both the `adder()` and `subtractor()`functions are decorated with the decorator ```double() ```

### How does the code work?

1. We call the `adder()` and `subtractor()` functions with a print(), since the said functions don’t print by default (due to the `return` statement).
2. The python interpreter sees the decorator `@double` and calls it.
3. Since the decorator returns the inner function `inner_func()`, the interpreter executes it.
4. The decorator takes an argument `my_func`, which is always the function on which the decorator is applied, ie.. in our case `my_case == adder()`and `my_case == subtractor()`.
5. The inner function within the decorator takes arguments, which are the arguments passed to the functions that are being decorated. ie.. Any arguments passed to `adder()` and `subtractor()`are passed to `inner_func()`.
6. The statement `return 2 * my_func(a, b)` returns the value of :
1. 2 x `adder(10, 20)`
2. 2 x `subtractor(6, 1)`

### Output:

```# python decorators-2.py
60
10
```

Inbuilt decorators such as @staticmethod and @classmethod will be discussed in an upcoming post.

NOTE: To see how decorators are syntactically sugar coated, read Magic methods and Syntactic sugar in Python

# range() and enumerate()

The usual way to iterate over a range of numbers or a list in python, is to use range().

Example 0:

```colors = ["yellow", "red", "blue", "white", "black"]

for i in range(len(colors)):
print(i, colors[i])
```

This should output:

```(0, 'yellow')
(1, 'red')
(2, 'blue')
(3, 'white')
(4, 'black')
```

print(), by default, returns a tuple. If we want to print it in a more presentable way, we’ll need to find the indice at which each value is, and print that as well. Re-write the code a bit, to achieve the desired output:

```colors = ["yellow", "red", "blue", "white", "black"]

for i in range(len(colors)):
color = colors[i]
print("%d: %s" % (i, color))
```

This should print:

```0: yellow
1: red
2: blue
3: white
4: black
```

We can see that the above output starts with ‘0’ since python starts counting from ‘0’. To change that to ‘1’, we’ll need to tweak the print() statement.

```colors = ["yellow", "red", "blue", "white", "black"]

for i in range(len(colors)):
color = colors[i]
print("%d: %s" % (i + 1, color))
```

This should print:

```1: yellow
2: red
3: blue
4: white
5: black
```

Even though the above code snippet isn’t that complex, a much better way exists to do this. This is where the builtin function enumerate() comes in.

enumerate() returns a tuple when passed an object which supports iteration, for example, a list. It also supports a second argument named ‘start‘ which default to 0, and can be changed depending on where to start the order. We’ll check what ‘start‘ is towards the end of this article.

```colors = ["yellow", "red", "blue", "white", "black"]
print(list(enumerate(colors)))
```

This returns a list of a tuples.

```[(0, 'yellow'), (1, 'red'), (2, 'blue'), (3, 'white'), (4, 'black')]
```

To get to what we desire, modify it as:

```for i, color in enumerate(colors):
print('%d: %s' % (i, color))
```

This outputs:

```0: yellow
1: red
2: blue
3: white
4: black
```

Remember that we talked about that enumerate() takes a second value named ‘start‘ which defaults to ‘0’? Let’s check how that’ll help here.

The above output starts with ‘0’. ‘start’ can help to change that.

```for i, color in enumerate(colors, start=1):
print('%d: %s' % (i, color))
```

This should change the output as:

```1: yellow
2: red
3: blue
4: white
5: black
```