# Python data types

## Contents

# Python data types¶

Python can be a little strange in providing lots of data types, dynamic type allocation, and some interconversion.

## Numbers¶

Integers, Floating point numbers, and complex numbers are available automatically.

```
f = 1.0
i = 1
print (f, i)
print
print ("Value of f is {}, value of i is {}".format(f,i))
print
print ("Value of f is {:f}, value of i is {:f}".format(f,i))
```

```
## BUT !!
print ("Value of f is {:d}, value of i is {:f}".format(f,i))
```

```
c = 0.0 + 1.0j
print (c)
print ("Value of c is {:f}".format(c))
print ("Value of c**2 is {:f}".format(c**2))
```

Notes: The `math`

module needs to be imported before you can use it.

```
import math
math.sqrt(f)
```

```
math.sqrt(c)
```

```
math.sqrt(-1)
```

```
import cmath
print (cmath.sqrt(f))
print (cmath.sqrt(c))
print (cmath.sqrt(-1))
```

### numbers as objects¶

Virtually everything in python is an object. This means that it is a **thing** that can have multiple copies made (all of which behave independently) and which knows how to **do** certain operations on itself.

For example, a floating point number knows certain things that it can do as well as simply “being” a number:

```
f
```

```
help(f)
```

```
print (f.is_integer() ) # Strange eh ?
print (f.conjugate())
print (c.conjugate())
print (f.__truediv__(2.0) ) # This looks odd, but it is the way that f / 2.0 is implemented underneath
```

```
1.0.__truediv__(2.0)
```

## Strings¶

```
s = 'hello'
print (s[1] )
print (s[-1])
print (len(s) )
print (s + ' world')
```

```
ss = "\t\t HellO \n \t\t world\n \t\t !!!\n\n "
print (ss)
print (ss.partition(' '))
```

```
"Hello World".lower()
```

```
print (s[-1]," ", s[0:-1])
```

But one of the problems with strings as data structures is that they are immutable. To change anything, we need to make copies of the data

```
s[1] = 'a'
```

## tuples and lists and sets¶

Tuples are bundles of data in a structured form but they are not vectors … and they are immutable

```
a = (1.0, 2.0, 0.0)
b = (3.0, 2.0, 4.0)
print (a[1])
print (a + b)
```

```
print (a-b)
```

```
print (a*b)
```

```
print( 2*a)
```

```
a[1] = 2
```

```
e = ('a', 'b', 1.0)
2 * e
```

Lists are more flexible than tuples, they can be assigned to, have items removed etc

```
l = [1.0, 2.0, 3.0]
ll = ['a', 'b', 'c']
lll = [1.0, 'a', (1, 2, 3), ['f', 'g', 'h']]
print (l)
print (ll)
print (l[2], ll[2])
print (2*l)
print (l+l)
print (lll)
print (lll[3], " -- sub item 3 --> ", lll[3][1])
```

```
2.0*l
```

```
l[2] = 2.99
l
```

```
l.append(3.0)
print (l)
```

```
ll += 'b'
print (ll)
ll.remove('b') # removes the first one !
print (ll)
```

```
l += [5.0]
print ("1 - ", l)
l.remove(5.0)
print ("2 - ", l)
l.remove(3.0)
print( "3 - ", l)
l.remove(4.0)
print ("4 - ", l)
```

Sets are special list-like collections of unique items. NOTE that the elements are not ordered (no such thing as `s[1]`

```
s = set([6, 5, 4, 3, 2, 1, 1, 1, 1])
print (s)
s.add(7)
print (s)
s.add(1)
s2 = set([5, 6, 7, 8, 9, 10, 11])
s.intersection(s2)
s.union(s2)
```

## Dictionaries¶

These are very useful data collections where the information can be looked up by name instead of a numerical index. This will come in handy as a lightweight database and is commonly something we need to use when using modules to read in data.

```
d = { "item1": ['a', 'b', 'c'], "item2": ['c', 'd', 'e']}
print (d["item1"])
print (d["item1"][1])
d1 = {"Small Number":1.0, "Tiny Number":0.00000001, "Big Number": 100000000.0}
print (d1["Small Number"] + d1["Tiny Number"])
print (d1["Small Number"] + d1["Big Number"])
print (d1.keys())
for k in d1.keys():
print ("{:>15s}".format(k), " --> ", d1[k])
```

More useful is the fact that the dictionary can have as a key, anything that can be converted using the `hash`

function into a unique number. Strings, obviously, work well but anything immutable can be hashed:

```
def hashfn(item):
try:
h = hash(item)
print ("{}".format(str(item)), " --> ", h)
except:
print ("{}".format(item), " --> unhashable type {}".format((type(item))))
return
hashfn("abc")
hashfn("abd")
hashfn("alphabeta")
hashfn("abcdefghi")
hashfn(1.0)
hashfn(1.00000000000001)
hashfn(2.1)
hashfn(('a', 'b'))
hashfn((1.0, 2.0))
hashfn([1, 2, 3])
import math
hashfn(math.sin) # weird ones !!
```

**Exercise**: *Build a reverse lookup table*

Suppose you have this dictionary of phone numbers:

```
phone_book = { "Achibald": ("04", "1234 4321"),
"Barrington": ("08", "1111 4444"),
"Chaotica" : ("07", "5555 1234") }
```

Can you construct a reverse phone book to look up who is calling from their phone number ?

**Solution:** Here is a possible solution for the simple version of the problem but this could still use some error checking (if you type in a wrong number)

```
# Name: ( area code, number )
phone_book = { "Achibald": ("04", "1234 4321"),
"Barrington": ("08", "1111 4444"),
"Chaotica" : ("07", "5555 1234") }
```

```
newdict = {}
for key in phone_book:
newdict[phone_book[key]] = key
newdict
```

```
letters_and_numbers = [['a', 1], ['b', 2], ['c', 3]]
```

```
[i**2 for i in range(10)]
```

```
phone_book.items()
```

```
%%timeit
{val:key for key, val in phone_book.items()}
```

```
for letter, number in letters_and_numbers:
print(number * letter)
```

```
# Name: ( area code, number )
phone_book = { "Achibald": ("04", "1234 4321"),
"Barrington": ("08", "1111 4444"),
"Chaotica" : ("07", "5555 1234") }
reverse_phone_book = {}
```

```
%%timeit
for key in phone_book.keys():
reverse_phone_book[phone_book[key]] = key
```

```
print (reverse_phone_book[('07', '5555 1234')])
```