Mastering Datatypes in Python
Tags: python, programming, codenewbie
Published: 2020-11-26 10:42:05

This topic might be a bore for non-enthusiasts and beginners alike :confused:, but really is quite easy and simple to pick up :hand_over_mouth:. Okay, before you criticize me for my demeanor, let me make it clear that the aim of this post is to **strengthen** the fundamentals of beginners, and for the experienced folks to brush up on their amazing skills!

A **datatype**, simply put, represents the type of data stored into a variable, in memory. These may be classified into 2 main categories: ***Built-in*** and ***User-defined***. For now, let us focus on built-in datatypes only.

## **Built-in Datatypes**

Python provides a few built-in data types, which come preloaded when it is installed:
* None
* Numeric
* bool
* Sequences
* Sets
* Mappings (Dictionaries)

Let us look at each of these in detail! :smile:

### None Type

In Python, the '_None_' datatype represents an object which does not contain any value (similar to 'null' in Java). Python being a [**dynamically-typed language**](https://dev.to/mcscodes/python-preamble-127m), '_None_' is used less frequently.

### Numeric Type

These represent numbers, and are further subdivided into:

1. **_int_** - These represent integers (a number without any decimal point). For example, `a = -30` or `b = 289`.

2. **_float_** - These represent floating-point numbers (a number containing a decimal point). For example, `n = 25.69` or `x = 30.26e3`, where '_e_' or '_E_' represents _exponentiation to the power of 10_.

3. **_complex_** - These represent complex numbers written in the form `a+bj` or `a+bJ`. In this form, '_a_' is the real part of the number and '_b_' is the coefficient, while '_j_' or '_J_' is the square root of _-1_. For example,
`c1 = 1 - 0.5j` or `c2 = 2 + 5J`.

:thinking: ***You may find this interesting too :***
Python provides facilities to represent _Binary_, _Octal_ and _Hexadecimal_ numbers too. A binary number should be written with a prefix '**_0b_**' (zero and b) or '**_0B_**' (zero and B), octal numbers with a prefix of '**_0o_**' (zero and o) or '**_0O_**' (zero and O), and hexadecimal numbers with a prefix of '**_0x_**' (zero and x) or '**_0X_**' (zero and X).

### bool Type

This is a special datatype which represents either "**True**" or "**False**". It is used mainly in conditional statements to evaluate conditions and provide a logical answer (_True_ or _False_). Conditional statements will be covered in an upcoming post, but for now we only need to know that they help us decide the flow of control of our program depending on any given condition.

### Sequences in Python

These may be looked at as a group of elements or items. In Python, we have six types of sequences:

1. **_str_** - These represent strings (a group of characters) in Python. Strings are enclosed within double or single quotes, and even with triple double quotes or triple single quotes (called [**docstrings**](https://dev.to/mcscodes/comments-and-docstrings-in-python-gaf)). For example,
`word = "Hello"` or `name = "Mack"`.

2. **_bytes_** - These represent a group of byte numbers just like an array. A byte number is any positive number from 0 to 255 (inclusive). For example, `x = bytes([0, 10, 20, 30, 40, 50])` creates a bytes array having 6 elements.

3. **_bytearray_** - This is exactly similar to the _bytes_ datatype, the only difference being that this datatype is ***mutable*** (the elements of an array, once created, cannot be altered). We will look at _mutability_ later on so do continue reading! :grin:

4. **_list_** - Lists in Python are similar to arrays in C or Java, the main difference being a _list_ is capable of storing multiple types of objects, whereas an _array_ can only store one type of elements. Hence, we can see that lists are a very important datatype. For example, 
`names = ['John', 'Roy', 'Michelle', 'Sharon']` or
`misc = [1, 'Michael', 23.60, -30]`.

5. **_tuple_** - A _tuple_ is exactly similar to a _list_, the only main difference being that a _tuple_ is **immutable** whereas a _list_ is **mutable**. Hence, we can say that a _tuple_ is a **read-only list**. For example,
`names = ('John', 'Roy', 'Michelle', 'Sharon')` or
`misc = (1, 'Michael', 23.60, -30)`.

6. **_range_** - These represent a sequence of numbers. Similar to a _tuple_, the elements are not modifiable i.e., this datatype is **immutable**. These are generally used in loops and iterative structures. For example,
`r = range(10)`, the variable 'r' stores numbers from _0_ upto _9_.

:thinking: ***A key takeaway :***
From the above examples, we can see that a _list_ encloses elements within opening and closing square / box brackets ('**[**' and '**]**'), while a _tuple_ encloses elements within opening and closing parenthesis ('**(**' and '**)**').

### Sets in Python

A set is an **unordered** collection of elements. What does this mean? Well, whenever we create a sequence in Python, the order of the elements stays put (fixed). This is not true for sets. Whenever we create a set, the elements may not appear in the same order as they are entered. Moreover, a set **does not accept duplicate elements**. Sets are again sub-divided into two:

1. **_set_** - This is similar to an array and uses opening and closing curly brackets ('**{**' and '**}**') to store the elements. We can create a _set_ in two ways, for example, `s1 = {1, 2, 3, 4, 5}` or `s2 = set("Hello")`. The second method utilizes the '_set( )_' function to convert the letters of the string into a set.

2. **_frozenset_** - It is similar to the _set_ datatype, the only difference being, _frozenset_ datatype as the name suggests is **immutable**, while the _set_ datatype is **mutable**. We also have two ways to create a _frozenset_, we can first create a set and then pass it thorugh the _frozenset( )_ function `fs1 = frozenset(s1)` or directly pass another object (such as a string) to create a frozenset, `fs2 = frozenset("Hello")`.

### Mappings (Dictionaries) in Python

A **map** represents a group of elements in the form of **key-value pairs**. Understanding dictionaries in Python is pretty simple.

Imagine you are given a particular word (say "Photosynthesis") with a dictionary in hand, and you are said to locate the meaning of the word using the dictionary. Logically, we would start searching for the meaning, by first looking up the letters (starting from the first all the way upto the last one) until we find the word (starting from "P" in our case, followed by "h", "o", and all the way upto "s"). This way, we are able to quickly locate the meaning without having to look at the meaning of each and every word in the dictionary. In this case the word is considered as the "**key**" and the meaning is considered as the "**value**"

Similarly, the '**_dict_**' datatype in Python is utilized to store key-value pairs. The _keys_ and _values_ are separated using a colon (**:**) and every _key-value pair_ is separated using a comma (**,**). Finally, all the elements are to be enclosed within curly brackets (**{ }**).

Also, dictionaries are **mutable** and hence the elements stored in the dictionaries can be altered. For example,
`roll = {1: "Aaron", 2: "Adele", 3: "Mike", 4: "Ryan"}` or `d = {}` is an empty dictionary.

## **Mutability**

It is a property of an **object** by which it's elements can be altered after creation. Let us take a look at the following code:
```
lst = [1,2,3,4,5] # A list of 5 elements
print(lst) # [1,2,3,4,5] is printed
lst[0] = 10 # Changing value at index 0
print(lst) # [10,2,3,4,5] is printed
```
From the above code, we can make out that the value of the first element in the list (at index 0) was changed from '1' to '10'. We were able to do this because as we have seen earlier, _lists_ are **mutable**. Finally, let us look at another example:
```
tup = (1,2,3,4,5) # A tuple of 5 elements
tup[0] = 10 # Throws an error on the screen
```
As we can see, the second statement throws an error. Why is that? It is because _tuples_ (similar to _frozenset_ and _bytes_) are **immutable**. Any attempt to alter the elements, once the object has been created, is simply not allowed.