---
# System prepended metadata

title: Intro to Python - Part II

---

<a target="_blank" href="https://drive.google.com/file/d/1ryNRT-_TB12NMuxOK7gKEhgp9ICGrR_s/view?usp=sharing"><img src="https://www.tensorflow.org/images/colab_logo_32px.png" />Run in Google Colab</a>
<br />

# Intro to Python - Part II

In this notebook, we focus on **string**, **list**, and **dictionary** in Python.

## Strings

A string is a sequence of characters. You can access the characters one at a time with the bracket operator:

```python
fruit = 'banana'
letter_b = fruit[0]
letter_b
```

    'b'

The expression in brackets is called an **index**. The index indicates which character in the sequence you want. _Notice that the first letter has the index 0_.

**len()** is a built-in function that returns the number of characters in a string.

```python
len(fruit)
```

    6

### Traversal with loops

A lot of computations involve processing a string one character at a time. Often they start at the beginning, select each character in turn, do something to it, and continue until the end. This pattern of processing is called a traversal.

**Exercise**: _Write a function that takes a string as an argument and displays the letters backward._

```python
def reverse_str(s):
    index = len(s) - 1
    while index > -1:
        print(s[index], end='')
        index = index - 1

reverse_str('Hello Python')
```

    nohtyP olleH

Another way to write a traversal is with a `for` loop:

```python
for char in fruit:
    print(char)
```

    b
    a
    n
    a
    n
    a

Each time through the loop, the next character in the string is assigned to the variable char. The loop continues until no characters are left.

### String slices

A segment of a string is called a **slice**. Selecting a slice is similar to selecting a character:

```python
fruit[1:4]
```

    'ana'

```python
fruit[2:5]
```

    'nan'

The operator `[n:m]` returns the part of the string from the “n-eth” character to the “m-eth” character, including the first but excluding the last. This behavior is counterintuitive, but it might help to imagine the indices pointing between the characters

<img src="https://i.imgur.com/uZUDVyB.png" width="200ps" />

If you omit the first index (before the colon), the slice starts at the beginning of the string. If you omit the second index, the slice goes to the end of the string.

```python
fruit[:3]
```

    'ban'

```python
fruit[2:]
```

    'nana'

**Quiz**: _Given that `s` is a string, what does `s[:]` mean?_

### String methods

A **method** is similar to a function—it takes arguments and returns a value—but the syntax is different.

```python
name = "CoderSchool"
new_name = name.upper()
print('name:', name)
print('new_name:', new_name)
```

    name: CoderSchool
    new_name: CODERSCHOOL

This form of dot notation specifies the name of the method, `upper`, and the name of the string to apply the method to, `name`. A method call is called an **invocation**; in this case, we would say that we are invoking `upper` on the `name`.

Here below are some useful string methods:

```python
# all lowercase
name.lower()
```

    'coderschool'

```python
# check if the string starts with a specified value
name.startswith('Coder')
```

    True

```python
# check if the string ends with a specified value
name.endswith('school')
```

    False

```python
# str.split() method returns a list of strings that are separated by whitespace by default
quote = "To be or not to be"
words = quote.split()
words
```

    ['To', 'be', 'or', 'not', 'to', 'be']

```python
# str.split() can remove certain parts of an original string
date = '2020-20-02'
date.split('-')
```

    ['2020', '20', '02']

```python
# str.join() - sewing a list of strings up into one long string, using the string it was called on as a separator.
'-'.join(words)
```

    'To-be-or-not-to-be'

```python
numbers = ['100', '000', '000']
'.'.join(numbers)
```

    '100.000.000'

## Lists

Like a string, a **list** is a sequence of values. In a string, the values are characters; in a list, they can be any type. The values in a list are called **elements** or sometimes **items**.

```python
nums = [1, 2, 3, 4]
maths = ['statistics', 'calculus', 'linear algebra']
empty_list = []

print(len(nums))
print(len(maths))
print(len(empty_list))
```

    4
    3
    0

A list that contains no elements is called an empty list; you can create one with empty brackets, `[]`.

A list within another list is **nested**:

```python
matrix = [ [1, 2, 3], [4, 5, 6] ]
```

The syntax for accessing the elements of a list is the same as for accessing the characters of a string—the bracket operator. The expression inside the brackets specifies the index. Remember that the indices start at 0:

```python
print(nums[0])
print(maths[1])
print(matrix[0][2])
```

    1
    calculus
    3

The **slice operator [n:m]** also works on lists:

```python
letters = ['a', 'b', 'c', 'd', 'e', 'f']

print(letters[1:3])
print(letters[:3])
print(letters[3:])
print(letters[::-1])
```

    ['b', 'c']
    ['a', 'b', 'c']
    ['d', 'e', 'f']
    ['f', 'e', 'd', 'c', 'b', 'a']

### List methods

**append()** adds a new element to the end of a list:

```python
letters.append('g')
letters
```

    ['a', 'b', 'c', 'd', 'e', 'f', 'g']

**extend()** takes a list as argument and appends all of the elements:

```python
letters.extend(['h', 'i', 'j'])
letters
```

    ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']

**pop()** modifies the list and returns the element that was removed. If you don’t provide an index, it deletes and returns the last element.

```python
last_letter = letters.pop()

print(last_letter)
print(letters)
```

    j
    ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']

```python
second_letter = letters.pop(1)

print(second_letter)
print(letters)
```

    b
    ['a', 'c', 'd', 'e', 'f', 'g', 'h', 'i']

If you don’t need the removed value, you can use the **del** operator:

```python
del letters[0]

print(letters)
```

    ['c', 'd', 'e', 'f', 'g', 'h', 'i']

## Dictionaries

Dictionaries are a built-in Python data structure for mapping **keys** to **values**.

To define a Dictionary, we use curly braces `{}`

```python
numbers = {'one':1, 'two':2, 'three':3}
```

In this case `'one'`, `'two'`, and `'three'` are the **keys**, and 1, 2 and 3 are their corresponding **values**.

Values are accessed via square bracket syntax `[]` similar to indexing into lists and strings.

```python
numbers['one']
```

    1

We can use the same syntax to add another `key-value` pair:

```python
numbers['eleven'] = 11

print(numbers)
```

    {'one': 1, 'two': 2, 'three': 3, 'eleven': 11}

Or to change the value associated with an existing key:

```python
numbers['one'] = 'eins'

print(numbers)
```

    {'one': 'eins', 'two': 2, 'three': 3, 'eleven': 11}

A for loop over a dictionary will loop over its keys:

```python
for k in numbers:
    print(k, '=', numbers[k])
```

    one = eins
    two = 2
    three = 3
    eleven = 11

### Dictionaries methods

We can access a collection of all the keys or all the values with **`dict.keys()`** and **`dict.values()`**, respectively.

```python
print(numbers.keys())
print(numbers.values())
```

    dict_keys(['one', 'two', 'three', 'eleven'])
    dict_values(['eins', 2, 3, 11])

The very useful **`dict.items()`** method lets us iterate over the keys and values of a dictionary simultaneously.

```python
numbers.items()
```

    dict_items([('one', 'eins'), ('two', 2), ('three', 3), ('eleven', 11)])

```python
for key, value in numbers.items():
    print(key, value)
```

    one eins
    two 2
    three 3
    eleven 11

### Example

Write a function called `letters_histogram()` that takes a string as an argument and return a dictionary that shows how many times each letter appears.

For example `letters_histogram('aabbc')` should return `{'a': 2, 'b': 2, 'c': 1}`

```python
def letters_histogram(s):
    d = {}
    for letter in s:
        if letter not in d:
            d[letter] = 1
        else:
            d[letter] = d[letter] + 1
    return d
```

```python
letters_histogram('aabbc')
```

    {'a': 2, 'b': 2, 'c': 1}

## List and Dictionary comprehension

**List comprehensions** are one of Python's most beloved and unique features.

- It provides a concise way to create lists.
- It consists of squarre brackets containing an expression followed by a `for` clause, then zero or more `for` or `if` clauses. The expressions can be anything, meaning you can put in all kinds of objects in lists.
- The result will be a new list resulting from evaluating the expression in the context of the `for` and `if` clauses which follow it.

The list comprehension starts with a `[` and `]`, to help you remember that the result is going to be a list.

The basic syntax is:

```python
[ expression for item in list if conditional ]
```

```python
even_numbers = [x for x in range(5) if x % 2 == 0]  # [0, 2, 4]
squares      = [x * x for x in range(5)]            # [0, 1, 4, 9, 16]
even_squares = [x * x for x in even_numbers]        # [0, 4, 16]
```

**Dictionary comprehension** is equivalent:

```python
square_dict = {x: x * x for x in range(5)}  # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
square_set  = {x * x for x in [1, -1]}      # {1}
```

A list comprehension can include multiple `for`:

```python
pairs = [(x, y)
         for x in range(10)
         for y in range(10)]   # 100 pairs (0,0) (0,1) ... (9,8), (9,9)
```

Another cool thing is the **interpretation**

```python
new_list = [expression(i) for i in old_list if filter(i)]
```

- **new_list**: The new list (result).

- **expression(i)**: Expression is based on the variable used for each element in the old list.

- **for i in old_list**: The word for followed by the variable name to use, followed by the word in the
  old list.

- **if filter(i)**: Apply a filter with an If-statement.

The easiest way to understand them is probably to just look at a few examples:

```python
job_titles = ['CEO', 'CTO', 'COO', 'Developer', 'Product Manager']

# Write a function that takes in a list of job titles and return list of titles with length less than 4
def short_titles(lst):
    result = []
    for title in lst:
        if len(title) < 4:
            result.append(title)
    return result

short_titles(job_titles)
```

    ['CEO', 'CTO', 'COO']

Here is a solution using list comprehension:

```python
def short_titles_2(lst):
    return [title for title in lst if len(title) < 4]
```

```python
short_titles_2(job_titles)
```

    ['CEO', 'CTO', 'COO']

Much better, right?

## Exercises

### Exercise 1

Write a function that takes a string as an argument, and count how many `a` letter appears in the string.

```python
def count_letter_a(s):
    # YOUR_CODE HERE
```

### Exercise 2

A palindrome is a word that is spelled the same backward and forward, like “noon” and “redivider”. Recursively, a word is a palindrome if the first and last letters are the same and the middle is a palindrome.

Write a function called `is_palindrome` that takes a string argument and returns `True` if it is a palindrome and `False` otherwise.

**Hint**: _A string slice can take a third index that specifies the "step size;" that is, the number of spaces between successive characters. A step size of 2 means every other character; 3 means every third, etc._

```
fruit = 'banana'
fruit[0:5:2]

'bnn'
```

_A step size of -1 goes through the word backwards, so the slice `[::-1]` generates a reversed string._

```python
def is_palindrome(s):
    # YOUR_CODE HERE
```

### Exercise 3

Write a function called middle that takes a list and returns a new list that contains all but the first and last elements. So `middle([1,2,3,4])` should return `[2,3]`.

```python
def middle(lst):
    # YOUR_CODE HERE
```

### Exercise 4

Write a function that takes a list of numbers and returns the cumulative sum; that is, a new list where the ith element is the sum of the first i+1 elements from the original list. For example, the cumulative sum of `[1, 2, 3]` is `[1, 3, 6]`.

```python
def cumulative_sum(lst):
    # YOUR_CODE HERE
```

### Exercise 5

Run this cell below to define the data:

```python
shoppingCart = [
  { 'id': 'A31', 'item': 'T-shirt', 'price': 9.9, 'quantity': 5 },
  { 'id': 'A32', 'item': 'Jacket', 'price': 99.9, 'quantity': 1 },
  { 'id': 'A33', 'item': 'Skirt', 'price': 19.9, 'quantity': 2 },
  { 'id': 'A34', 'item': 'Ankle Pant', 'price': 39.9, 'quantity': 3 },
  { 'id': 'A35', 'item': 'Polo shirt', 'price': 14.9, 'quantity': 3 },
  { 'id': 'A36', 'item': 'Chino Short', 'price': 29.9, 'quantity': 2 },
  { 'id': 'A37', 'item': 'Easy Short', 'price': 19.9, 'quantity': 2 },
]
```

- Write a function that returns an array of prices for each product in the cart (item price \* quantity).
- Write a function that returns the total price.

```python
def product_prices(cart):
    # YOUR_CODE_HERE

def total_price(cart):
    # YOUR_CODE_HERE
```

## References

- [CheckIO](http://www.checkio.org/): a gamified website containing programming tasks in Python 3
- [LearnPython](https://www.learnpython.org/): an interactive Python tutorial that is suitable for absolute beginners
- [Automate the Boring Stuff with Python](https://automatetheboringstuff.com/): small and practical programs to automate tasks on the computer
- [Think Python](https://greenteapress.com/thinkpython/html/index.html): how to think like a computer scientist
