# 8. Built-in Functions and Types

There are many functions and types (data structures) that are built into Python that you should reuse and try to not re-invent yourself. As you progress and study more into Python, you will acquire exposure to ever more utility functions and data structures available. Below are just some samples of what we think will be useful.

## 8.1. Types

The function `type()` returns the type of a value or variable.

``` 1s = 'test'
2i = 32
3f = 32.2
4b = False
5
6type_s = type(s)
7type_i = type(i)
8type_f = type(f)
9type_b = type(b)
10
11print(type_s)
12print(type_i)
13print(type_f)
14print(type_b)
```

The following functions converts data from one type to another.

• `str()` converts something into a string

• `int()` converts something to an integer

• `float()` converts something to a float

```1a = str(32)
2b = int('32')
3c = float('32.2')
4
5print(a)
6print(b)
7print(c)
```

The function `isinstance()` checks to see if a variable/value is an instance of a particular type.

```1a = isinstance('s', str)
2b = isinstance(3, int)
3c = isinstance(3.1415, float)
4d = isinstance(False, bool)
5
6print(a)
7print(b)
8print(c)
9print(d)
```

### 8.1.1. Exercise

Filter out the items in the list below for only integers, and create a new list from the integers.

```1data = [1, 2, 'test', 3, 88, True, False, 32.2, 88.5, 0]
```

Solution.

```1data = [1, 2, 'test', 3, 88, True, False, 32.2, 88.5, 0]
2numbers = [item for item in data if isinstance(item, int)]
3print(numbers)
```

## 8.2. Math

Use `sum()` to add up all the numbers in a collection.

```1numbers = [23, 3, 5, 8]
2total = sum(numbers)
3
4print(total)
```

Use `abs()` to get the absolute value of a number.

```1a = -9
2b = 9
3
4c = abs(a)
5d = abs(b)
6
7print(c)
8print(d)
```

Use `min()` and `max()` to find the minimum and maximum numbers in a collection, correspondingly.

```1numbers = [23, 3, 5, -8, 100]
2
3smallest = min(numbers)
4largest = max(numbers)
5
6print(smallest)
7print(largest)
```

Use `round()` to round numbers to certain precisions (number of decimal places).

```1a = 2.675255
2
3b = round(a, 1)
4c = round(a, 2)
5d = round(a, 3)
6
7print(b)
8print(c)
9print(d)
```

### 8.2.1. Exercise

The following is a list of lists. For each list, compute the average and store the averages into a new list.

```1data = [
2    [10, 23, 88, 32, 343, 88, 77],
3    [22, 20, 18, 23, 45, 77, 88],
4    [55, 77, 32, 38, 67, 21, 33]
5]
```

Solution.

```1data = [
2    [10, 23, 88, 32, 343, 88, 77],
3    [22, 20, 18, 23, 45, 77, 88],
4    [55, 77, 32, 38, 67, 21, 33]
5]
6
7averages = [sum(arr) / len(arr) for arr in data]
8print(averages)
```

## 8.3. Collections

The following functions create different collections.

• `list()` creates an empty list or converts a collection into a list

• `set()` creates an empty set or converts a collection into a set

• `tuple()` creates an empty tuple or converts a collection into a tuple

• `dict()` creates an empty dictionary

```1a = set([1, 2, 3, 4, 5, 1, 3])
2b = tuple(a)
3c = list(b)
4
5print(a)
6print(b)
7print(c)
```

The function `all()` will return `True` if all the elements in a collection are not null e.g. `None`. The function `any()` will return `True` if at least one lement in a collection is not null e.g. `None`.

```1numbers = [1, None, 2]
2
3print(all(numbers)) # False
4print(any(numbers)) # True
```

The function `len()` will return the number of elements in a collection.

```1numbers = [1, None, 2]
2total = len(numbers)
3print(total)
```

The function `sorted()` sorts a collection in ascending order. The function `reversed()` reverse sorts a collection (descending order). Note that `reversed()` returns an iterator and not a collection, and so we convert that iterator to a list with `list()`.

```1numbers = [32, 33, 2, 88, 31, 3]
2
3print(sorted(numbers))
4print(list(reversed(numbers)))
```

### 8.3.1. Exercise

The following is a list of lists. For each sub-list, sort it into a new list. Your resulting list should be a list of sorted lists.

```1data = [
2    [10, 23, 88, 32, 343, 88, 77],
3    [22, 20, 18, 23, 45, 77, 88],
4    [55, 77, 32, 38, 67, 21, 33]
5]
```

Solution.

```1data = [
2    [10, 23, 88, 32, 343, 88, 77],
3    [22, 20, 18, 23, 45, 77, 88],
4    [55, 77, 32, 38, 67, 21, 33]
5]
6
7sorted_data = [sorted(arr) for arr in data]
8print(sorted_data)
```

## 8.4. Range

The `range()` function generates a range of values. To generate a range of numbers from [0, 9], we would do the following.

```1numbers = list(range(10))
2print(numbers)
```

Note that `range()` does not create the numbers in the range until we start asking for the elements. `range()` is said to be a `generator function`. To get the actual numbers out, we have to convert the output of `range()` to a list.

We can specify the range of integers to output by supplying start and stop (exclusive) integers.

```1numbers = list(range(10, 21))
2print(numbers)
```

We can also specify what number to increment the current number by to step over numbers.

```1numbers = list(range(10, 21, 2))
2print(numbers)
```

### 8.4.1. Exercise

Generate a list of numbers from [0, 10] with only odd numbers.

Solution.

```1numbers = [num for num in range(11) if num % 2 != 0]
2print(numbers)
```

## 8.5. random

Functions are also stored in built-in Python `libraries` (or `modules`). A library is just a collection of code that logically and/or conceptually belong together. To use these functions, we must import the function from the library. One built-in Python library is the `random` library, which deals with generating random numbers. Let’s see how we can import the `randint()` function.

```1from random import randint
```

The syntax to import a function from a library is `from <library> import <function>`. After we import a function from a library, then we can use the function. The `randint()` function generates random number between two numbers (inclusive on both ends).

``` 1from random import randint
2
3# generate 3 random numbers in the range [0, 9]
4a = randint(0, 9)
5b = randint(0, 9)
6c = randint(0, 9)
7
8print(a)
9print(b)
10print(c)
```

The `choice()` function selects an element from a collection at random (all elements have an equal chance of being selected).

``` 1from random import choice
2
3numbers = [1, 2, 3, 4, 5, 6]
4
5a = choice(numbers)
6b = choice(numbers)
7c = choice(numbers)
8
9print(a)
10print(b)
11print(c)
```

The `random()` function generates a real number in the range [0, 1.0).

```1from random import random
2
3a = random()
4b = random()
5c = random()
6
7print(a)
8print(b)
9print(c)
```

### 8.5.1. Exercise

Generate 10 random numbers in the range [0, 9].

Solution.

```1from random import randint
2
3numbers = [randint(0, 9) for _ in range(10)]
```

### 8.5.2. Exercise

Generate 10 random numbers in the range [0, 9] only if the index associated with the random number is even.

Solution.

```1from random import randint
2
3numbers = [randint(0, 9) for i, _ in enumerate(range(10)) if i % 2 == 0]
4print(numbers)
```

### 8.5.3. Exercise

Simulate rolling a die 1000 times. Each time you roll an even number, you win. How many times did you win?

Solution.

```1from random import choice
2
3die = [1, 2, 3, 4, 5, 6]
4
5rolls = [choice(die) for _ in range(1000)]
6wins = sum([1 for roll in rolls if roll in {2, 4, 6}])
7print(wins)
```

### 8.5.4. Exercise

Simulate rolling two dice 1000. Each time you roll the dice and the sum of the outcomes is 7, you win. How many times did you win?

Solution.

```1from random import choice
2
3die = [1, 2, 3, 4, 5, 6]
4
5rolls = [choice(die) + choice(die) for _ in range(1000)]
6wins = sum([1 for roll in rolls if roll == 7])
7print(wins)
```

## 8.6. itertools

Check out the `itertools` module. There are a lot of utility functions you can use to make working with collections easier. The `cycle()` function endlessly cycles through a list of elements. Imagine you have a list of people that you need to assign to two teams, Team Red and Team Blue. How can we assign the list of people to these two teams?

```1from itertools import cycle
2
3colors = ['red', 'green']
4persons = ['Jack', 'John', 'Mary', 'Mandy', 'Moesha', 'Fatimah']
5
6teams = [(person, color) for person, color in zip(persons, cycle(colors))]
7print(teams)
```

What if we have a list of lists and wanted to flatten the list? Try the `chain()` function.

```1from itertools import chain
2
3teams = [['Jack', 'Mary'], ['John', 'Moesha'], ['Mandy', 'Fatimah']]
4persons = list(chain(*teams))
5print(persons)
```

What if we have a list of names and we wanted to group them by the first letter of the name? The `groupby()` function will produce a key-value pair for us, where the key is what we want to group by and the value is an iterable.

```1from itertools import groupby
2
3persons = ['Jack', 'Mary', 'John', 'Moesha', 'Mandy', 'Fatimah']
4
5by_first_letter = lambda name: name.lower()[0]
6name_map = {k: list(v) for k, v in groupby(persons, key=by_first_letter)}
7print(name_map)
```

If you have two lists and you want the resulting cross-product of the elements in those lists, try the `product()` function.

```1from itertools import product
2
3products = list(product([1, 2, 3], ['A', 'B', 'C']))
4print(products)
```

What if you have a list of elements and you want the n permutations or combinations of elements from that list? In permutation, order is important, so a pair of number 2 and 3 is not the same as 3 and 2. In combination, order is not important, so 2 and 3 is the same as 3 and 2. Remember, a combination lock is a misnomer, and should be a permutation lock. At any rate, try `permutations()` and `combinations()`.

```1from itertools import combinations, permutations
2
3# [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]
4print(list(permutations([1, 2, 3], 2)))
5
6# [(1, 2), (1, 3), (2, 3)]
7print(list(combinations([1, 2, 3], 2)))
```

### 8.6.1. Exercise

The Mega Millions is based on a player selecting 5 numbers from the range [1, 70] and one number from the range [1, 25]. How many combinations of numbers are possible?

Solution.

```1from itertools import combinations, product
2
3white_nums = list(range(1, 71, 1))
4gold_nums = list(range(1, 26, 1))
5
6n = sum([1 for play in product(combinations(white_nums, 5), gold_nums)])
7print(f'{n:,}') # 302,575,350
```

### 8.6.2. Exercise

The Power Ball is based on a player selecting 5 numbers from the range [1, 69] and one number from the range [1, 26]. How many combinations of numbers are possible?

Solution.

```1from itertools import combinations, product
2
3white_nums = list(range(1, 70, 1))
4red_nums = list(range(1, 27, 1))
5
6n = sum([1 for play in product(combinations(white_nums, 5), red_nums)])
7print(f'{n:,}') # 292,201,338
```

## 8.7. datetime

The `datetime` module has a lot of date functions and data structures (types) that we may use to speed up our ability to solve problems. Below, we show how to use the `date` type.

``` 1from datetime import date
2
3# create a date manually
4d = date(2019, 10, 31)
5print(d)
6
7# get the current date dynamically
8d = date.today()
9print(d)
10
11# get the date from a timestamp
12d = date.fromtimestamp(1555244364)
13print(d)
14
15# date difference
16a = date(2019, 10, 31)
17b = date(2019, 10, 30)
18diff = a - b
19print(diff)
```

The `time` type deals with the time.

```1from datetime import time
2
3# create time manually
4t = time(11, 59, 59)
5print(t)
6
7# create time with microseconds
8t = time(11, 59, 59, 999999)
9print(t)
```

The `datetime` type (not the module) deals with both date and time.

``` 1from datetime import datetime
2
3# get the date-time now
4dt = datetime.now()
5print(dt)
6
7# create a date-time manually
8dt = datetime(2019, 10, 31, 23, 55, 59, 100000)
9print(dt)
10
11# create a date-time from a string
12# the date format code is available at https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
13# strptime stands for string parse time
14dt = datetime.strptime('2019-10-31 23:55:59.999999', '%Y-%m-%d %H:%M:%S.%f')
15print(dt)
16
17# formatting date-time
18# strftime stands for string format time
19s = datetime.strftime(dt, '%m-%d-%Y %H:%M:%S.%f %p')
20print(s)
21
22# subtract dates, datetime.timedelta
23a = datetime(2019, 10, 31, 23, 55, 59, 100)
24b = datetime(2018, 10, 31, 23, 55, 59, 100)
25c = a - b
26print(c)
```

The `timedelta` type enables us to do math operations on durations.

```1from datetime import timedelta
2
3# compute difference between durations using timedelta
4a = timedelta(weeks=2, days=2, hours=1, seconds=30)
5b = timedelta(weeks=1, days=2, hours=1, seconds=30)
6diff = a - b
7print(diff)
8
```

The `calendar` module is very useful. We can use it to get the number of days for any particular month and year combination.

```from calendar import monthrange

start, stop = monthrange(2011, 2)
print(f'start = {start}, stop = {stop}')
```

### 8.7.1. Exercise

Write a program to ask the user for a year. After you get the year:

• print out the number of days per month for that year

• print out the total number of days in that year

• print out the total number of months with 31 days

• print out the total number of months with even days

• print out the total number of months with odd days

Solution.

```from calendar import monthrange

year = int(input('year: '))

data = [(year, month, monthrange(year, month)[1]) for month in range(1, 13)]

print(f'number of days per month in {year}')
for y, m, d in data:
print(f'{y}-{m}: {d}')

print('-' * 15)

n = sum([days for *_, days in data])
print(f'total days in {year}: {n}')

print('-' * 15)

n = sum([1 for *_, days in data if days == 31])
print(f'total months in {year} with 31 days: {n}')

print('-' * 15)

n = sum([1 for *_, days in data if days % 2 == 0])
print(f'total months in {year} with even total number of days: {n}')

print('-' * 15)

n = sum([1 for *_, days in data if days % 2 != 0])
print(f'total months in {year} with odd total number of days: {n}')
```

## 8.8. pathlib

For interacting with files, try the `pathlib` module. Below, we create a collection of all Python files.

```1import pathlib
2
3py_files = pathlib.Path('../').glob('**/*.py')
4
5for f in py_files:
6    print(f)
```

## 8.9. urllib

If you need to interact with websites, try the `urllib` module.

``` 1import urllib.request
2import urllib.parse
3
4# simple request
5with urllib.request.urlopen('https://www.oneoffcoder.com/') as response:
7    print(content)
8
9# request with query string parameters
10query_string_data = {
11    'q': 'oneoffcoder',
12    'sourceid': 'chrome',
13    'ie': 'UTF-8'
14}
15query_string = urllib.parse.urlencode(query_string_data)
17data = urllib.request.urlopen(url)
18
20query_string_data = {
21    'q': 'oneoffcoder',
22    'sourceid': 'chrome',
23    'ie': 'UTF-8'
24}
25query_string = urllib.parse.urlencode(query_string_data).encode('ascii')
26
27headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64)'}
29
31with urllib.request.urlopen(req) as response:
```

### 8.9.1. Exercise

Write a program to get the contents of `https://www.oneoffcoder.com`. How many times did the vowels a, e, i, o and u appear?

Solution.

```import urllib.request

vowels = {'a', 'e', 'i', 'o', 'u'}

with urllib.request.urlopen('https://www.oneoffcoder.com/') as response:

n = sum([1 for letter in content if chr(letter) in vowels])
print(f'total vowels: {n}')
```

### 8.9.2. Exercise

Write a program to get the contents of `https://www.oneoffcoder.com`.

• What is the number of times the each character in the range [a, z] appeared?

• What is the number of times each special characters (characters not in the range [a, z]) appeared?

Solution.

```import urllib.request
from itertools import groupby
import string

letters = set(list(string.ascii_lowercase))

with urllib.request.urlopen('https://www.oneoffcoder.com/') as response:

is_valid = lambda letter: chr(letter) in letters
get_key = lambda letter: letter

# letters in [a, z]
counts = sorted([chr(letter) for letter in content if is_valid(letter)], key=get_key)

for k, v in groupby(counts, key=get_key):
print(f'{k} => {len(list(v))}')

print('-' * 15)

# special characters not in [a, z]
counts = sorted([chr(letter) for letter in content if not is_valid(letter)], key=get_key)

for k, v in groupby(counts, key=get_key):
print(f'{k} => {len(list(v))}')
```