# Week 5 - Abstractions

## Table of Contents

## Container Types

Remember back in week 1 when I said that there were “other container types that we will get into later”? Well, here they are:

- List type: A
*mutable*container. Lists use*square brackets*.`# notice that lists can hold all kinds of elements, including other containers >>> ["Hello!", 2, True, [1], (0, 1)] ["Hello!", 2, True, [1], (0, 1)]`

- Tuple type: An
*immutable*container. Tuples use*round brackets*.^{1}`# same with tuples! >>> ("Hello!", 2, True, [1], (0)) ("Hello!", 2, True, [1], (0))`

### List Operations

Here are some of the most commonly-used list operations, assuming `lst`

is a list:

`lst[start:end:count]`

: Creates*a copy*of the list from starting position`start`

to ending position`end`

, taking every`count`

th element.`start`

defaults to`0`

,`end`

defaults to`len(lst)`

, and`count`

defaults to`1`

when each is not specified.`lst.append(x)`

: Appends x to the end of the list.`lst.extend(x)`

: Extends x, an iterable, to the end of`lst`

.- This is equivalent to appending each item from x to
`lst`

. - Or roughly in code:
`for item in a: lst.append(item)`

- This is equivalent to appending each item from x to
`lst.insert(pos, x)`

: Inserts`x`

at`pos`

position.`lst.remove(x)`

: Removes first item whose value is equal to`x`

`lst.pop(index)`

: Removes item whose index is`index`

from`lst`

.

For a full list of list operations, check the Python reference.

## An Aside on Mutability

You may have noticed that there wasn’t a section on tuple methods. This is because tuples are *immutable*, resulting in them only having two methods: `index`

and `count`

. ^{2}

Immutability is a feature of certain objects in Python where the value of the object cannot be changed after being declared. This means that in the case of tuples, no matter what you do, once the tuple is declared, its values are final and unchangeable.

You might ask, isn’t the following mutating the variable?

```
>>> a = (1, 2)
>>> a = (2, 3)
>>> a
(2, 3)
```

It’s not. The tuple itself is not changed; instead, the name `a`

is *rebound* to a different value.

## Abstractions: An Abstract

Programmers are, by nature, lazy people. We try to simplify things, to the point where we try to abstract away components. Much like how driving a car doesn’t require knowing how the car works under the hood, *abstractions* allow programmers to write code that uses parts of other code without knowing how it works.

## A Motivation for Abstractions

Let’s take the example of a coordinate pair. This can be represented by a two-element tuple, like this: `(x, y)`

. Suppose we were given these two points, `home`

and `school`

, and were told to create a `distance`

function to compute the distance between any two points. This might be a valid function:

```
>>> home = (0, 0) # I'm the center of the world
>>> school = (3, 4)
# Let's make a function to compute the distance betweeen two points:
>>> def distance(a, b):
x_distance = a[0] - b[0]
y_distance = a[1] - b[1]
pythagorean_distance = sqrt(x_distance ** 2 + y_distance ** 2)
return pythagorean_distance
>>> distance(home, school)
5.0
```

So far, this seems great! It looks like we can perform all sorts of computations to these coordinate objects. However, one problem will quickly arise.

Suppose the container type of these coordinates is changed. Instead of a tuple of `(x, y)`

, we’re now given a tuple of `(location_name, x, y)`

(making our locations `("home", 0, 0)`

and `("school", 3, 4)`

). Our implementation of distance is now suddenly broken! This gives a motivation for *abstractions*

## A Functional Introduction to Abstractions

Now, with the power of abstractions, let’s say we’re given this *functional interface*:

`make_location(location_name, x, y) -> location`

: A function that takes in a location name, an x coordinate, and a y coordinate, and returns a*location object*.`x_location(location) -> x`

: A function that takes in a location object and returns its x location.`y_location(location) -> y`

: A function that takes in a location object and returns its y location.`location_name(location) -> name`

: A function that takes in a location object and returns its name.

With this, let’s make the `distance`

function again:

```
>>> def distance(a, b):
x_distance = x_location(a) - x_location(b)
y_distance = y_location(a) - y_location(b)
pythagorean_distance = sqrt(x_distance ** 2 + y_distance ** 2)
return pythagorean_distance
```

And let’s try it on our `home`

and `school`

, also defined below:

```
>>> home = make_location("home", 0, 0)
>>> school = make_location("school", 3, 4)
>>> distance(home, school)
5.0
```

Note that this abstraction makes it so that *the distance function works regardless of the implementation of the abstraction itself* !

Two valid implementations of the abstractions could be as follows; notice that although the implementations are different, **the distance function still works!**:

##
### Implementation 1

```
### Abstraction ###
>>> def make_location(location_name, x, y):
return (location_name, x, y)
>>> def x_location(location):
return location[1]
>>> def y_location(location):
return location[2]
>>> def location_name(location):
return location[0]
### End Abstraction ###
>>> def distance(a, b):
x_distance = x_location(a) - x_location(b)
y_distance = y_location(a) - y_location(b)
pythagorean_distance = sqrt(x_distance ** 2 + y_distance ** 2)
return pythagorean_distance
>>> home = make_location("home", 0, 0)
>>> school = make_location("school", 3, 4)
>>> distance(home, school)
5.0
```

##
### Implementation 2

```
### Abstraction ###
>>> def make_location(location_name, x, y):
return (location_name, (x, y))
>>> def x_location(location):
return location[1][0]
>>> def y_location(location):
return location[1][1]
>>> def location_name(location):
return location[0]
### End Abstraction ###
>>> def distance(a, b):
x_distance = x_location(a) - x_location(b)
y_distance = y_location(a) - y_location(b)
pythagorean_distance = sqrt(x_distance ** 2 + y_distance ** 2)
return pythagorean_distance
>>> home = make_location("home", 0, 0)
>>> school = make_location("school", 3, 4)
>>> distance(home, school)
5.0
```