Author: James Bourbeau

In [1]:

```
%load_ext watermark
%watermark -d -v
```

Let's get a feel for some of the built-in types in python. Specifically, we'll discuss some of the most commonly used types, how to change the type of an object, and how some numeric operations behave between different types. If you would like to know the type of something, you can always use python's built-in `type()`

function.

In python there are three basic numerical types: integers, floating-point, and complex numbers. These types are denoted by `int`

, `float`

, and `complex`

, repsectively. Generally speaking, integers represent whole numbers ( ...,-2, -1, 0, 1, 2,...), floats represent decimal numbers, and complex are, well, complex numbers (where the real and imaginary parts are both floats). Information regarding the precision of floating-point can be found via

In [2]:

```
import sys
sys.float_info
```

Out[2]:

Numbers without a decimal point produce integers, while number that include a decimal (or an exponent sign) produce floats.

In [3]:

```
a = 2
type(a)
```

Out[3]:

In [4]:

```
b = 6.47
type(b)
```

Out[4]:

In [5]:

```
c = 9e8
type(c)
```

Out[5]:

A complex number can be created by including a 'j' or 'J' in a number. The corresponding real and imaginary parts of the complex number can be accessed using `real`

and `imag`

attributes.

In [6]:

```
z = 7 + 4.3j
type(z)
```

Out[6]:

In [7]:

```
z.real
```

Out[7]:

In [8]:

```
z.imag
```

Out[8]:

Note that the real and imaginary parts for the `complex`

type are floating-point numbers--regardless of whether or not there is a decimal point.

There are other built-in numeric types such as `fractions`

and `decimal`

, but I find `int`

, `float`

, and `complex`

to be the most commonly used.

Booleans are actually a subtype of integers with values represent one of two constant values: `True`

or `False`

. More will be discussed about booleans later on when we discuss type casting.

In [9]:

```
n = True
type(n)
```

Out[9]:

In [10]:

```
p = False
type(p)
```

Out[10]:

Some commonly used sequence types are the list, tuple, and string. Denoted by `list`

, `tuple`

, and `str`

, respectively. These can be thought of as an ordered containers that can store several different items. It is important to note that the items that are stored in a list or tuple need not be of the same type.

Lists are can be constructed in several ways. However, using square brackets with list items seperated by commas is very common.

In [11]:

```
d = [1, 2.3, 5, 74.7]
type(d)
```

Out[11]:

Tuples can be construced similarly, but with parenthesis instead of square brackets.

In [12]:

```
f = (83.2, -4 ,5e7)
type(f)
```

Out[12]:

One weird quirk is that a tuple with a single item needs to have a trailing comma, e.g.

In [13]:

```
f = (83.2,)
print(f)
type(f)
```

Out[13]:

If this trailing comma is left out, then python will assume you don't actually want a tuple and will assign whatever the single item is in parenthesis to your variable. For example,

In [14]:

```
f = (83.2)
print(f)
type(f)
```

Out[14]:

Each item in a sequence has a number to represent its location in the sequence. This number, called the index, starts at 0 for the first item, 1 for the second item, and so on. One can access an individual item in a sequence by using square brackets and the item's index. For example, the first item in the `d`

list is

In [15]:

```
d[0]
```

Out[15]:

The second item in the `d`

list is

In [16]:

```
d[1]
```

Out[16]:

The third item in the `d`

list is

In [17]:

```
d[2]
```

Out[17]:

and so on...

The length of a sequence, the number of items is contains, can be found with the built-in `len()`

function.

In [18]:

```
print(d)
len(d)
```

Out[18]:

Up to this point, it may seem like lists and tuples aren't any different. They are both containers that can hold items, you can access the items with an index, etc. How are these things different? The difference between the `list`

type and the `tuple`

type is that **lists are mutable, while tuples are immutable**. Mutable objects are objects that can be changed in place, while immutable objects cannot be changed in place. Let's look at an example.

Let's create a list

In [19]:

```
g = [1, 2, 3, 4]
print(g)
```

Now let's modify the list in place. That is, let's try to change the items in the list without creating a whole new list.

In [20]:

```
g[0] = 99
print(g)
```

As you can see, there wasn't a problem here. We assigned to the variable `g`

the list `[1, 2, 3, 4]`

, then modified the first item in the `g`

list to be the number `99`

. This is an example of a mutable object—it can be changed. Let's try the same thing with a `tuple`

now.

In [21]:

```
g = (1, 2, 3, 4)
print(g)
```

In [22]:

```
g[0] = 99
print(g)
```

We got this error because tuples are immutable—they can't be modified once they're created.

Strings are another sequence type and are used to represent a sequence of characters. Strings can be created by enclosing characters in either single or double quotes.

In [23]:

```
g = 'pizza'
type(g)
```

Out[23]:

In [24]:

```
h = "jamesbond007"
type(h)
```

Out[24]:

Because a string is a sequence, it can also be index just like lists and tuples.

In [25]:

```
h[0]
```

Out[25]:

In [26]:

```
h[1]
```

Out[26]:

You can also find out how many characters are in a string using the `len()`

function

In [27]:

```
len(h)
```

Out[27]:

In python, there is only one mapping type: the dictionary. It is denoted by `dict`

. Dictionaries are unordered containers for key-value pairs. That is, for each key in a dictionary there is an associated value. Dictionaries are created by placing comma-separated key-value pairs inside curly brackets `{}`

. For a key-value pair, the key and corresponding value are seperated by a colon, `:`

.

An example might help...

In [28]:

```
k = {'key1': 23, 'key2': -53.2, 'key3': 'Tokyo'}
type(k)
```

Out[28]:

Here, the dictionary keys are 'key1', 'key2', and 'key3', with corresponding values of 23, -53.2, and 'Tokyo'. In a similar way to sequences, you can access the values in a dicionary by giving the corresponding key in square brackets.

In [29]:

```
k['key1']
```

Out[29]:

In [30]:

```
k['key2']
```

Out[30]:

In [31]:

```
k['key3']
```

Out[31]:

The size of a dictionary, the number of key-value pairs is contains, can be found with the built-in `len()`

function.

In [32]:

```
len(k)
```

Out[32]:

It is important to note that in the previous example all the keys were strings, but this doesn't have to be the case. The only restriction on keys is that they be an *immutable type*. For example, the following is also an acceptable dictionary.

In [33]:

```
m = {-23: [1, 2, 3, 4], 'desk': 3.2, 7.12: (-3, 'bird')}
```

In [34]:

```
m[-23]
```

Out[34]:

In [35]:

```
m['desk']
```

Out[35]:

In [36]:

```
m[7.12]
```

Out[36]:

Let see what happens if I try to contruct a dictionary with a list key

In [37]:

```
n = {[1, 2, 3]: 'WOO'}
```

Whoops lists are mutable! So remember to always use immutable objects for dictionary keys!

Sometimes it can be useful to manually change the type of an object. In python, this so-called 'type-casting' can be accomplished using several built-in functions:

`int()`

—casts to integer`float()`

—casts to float`str()`

—casts to string`bool()`

—casts to boolean

Let's see it in action

Casting integers to floats is fairly straight-forward

In [38]:

```
a = float(2)
print(a)
type(a)
```

Out[38]:

When casting a float to an integer, python will round down to the nearest integer

In [39]:

```
b = int(78.81)
print(b)
type(b)
```

Out[39]:

You can even cast a number to a string! (This effectively just returns the number in quotes)

In [40]:

```
c = str(-1324.1)
print(c)
type(c)
```

Out[40]:

Things get a little less straight-forward when casting to `bool`

. Below are the `bool`

casting rules:

- Numbers: Zero of any numeric type, for example,
`0`

,`0.0`

,`0j`

, cast to`False`

. Everything else casts to`True`

. - Sequences: Any empty sequence, for example,
`()`

,`[]`

,`''`

, cast to`False`

. Everything else casts to`True`

. - Mapping: The empty dictionary,
`{}`

, casts to`False`

. Everything else casts to`True`

.

Here are some examples:

In [41]:

```
bool(0)
```

Out[41]:

In [42]:

```
bool(178.3)
```

Out[42]:

In [43]:

```
bool([])
```

Out[43]:

In [44]:

```
bool([1, 2, 3, 4, 5])
```

Out[44]:

In [45]:

```
bool({})
```

Out[45]:

In [46]:

```
bool({'key': 'value'})
```

Out[46]:

The following numerical operations are supported for numeric types:

- Addition:
`+`

- Subtraction:
`-`

- Multiplication:
`*`

- Division:
`/`

- Floored division:
`//`

- Exponentiation:
`**`

Some examples...

In [47]:

```
1+1
```

Out[47]:

In [48]:

```
10-3
```

Out[48]:

In [49]:

```
3*5.0
```

Out[49]:

In [50]:

```
5/2
```

Out[50]:

In [51]:

```
5//3
```

Out[51]:

In [52]:

```
9.0**2
```

Out[52]:

When performing numeric operations, the type of numbers *does* matter. According to the Python Software Foundation:

Python fully supports mixed arithmetic: when a binary arithmetic operator has operands of different numeric types, the operand with the 'narrower' type is widened to that of the other, where integer is narrower than floating point, which is narrower than complex.

So, when you have a numeric operation with mixed numeric types, say adding an `int`

and a `float`

, the result will have the 'widest' type of the two numbers, in this case `float`

. The convention is `int`

is the most narrow type, `float`

is wider than `int`

, and `complex`

is wider than `float`

.