# Python Programming/Data types

Data types determine whether an object can do something, or whether it
just would not make sense. Other programming languages often determine
whether an operation makes sense for an object by making sure the object
can never be stored somewhere where the operation will be performed on
the object (this type system
is called static typing). Python does not do that. Instead it stores the
type of an object with the object, and checks when the operation is
performed whether that operation makes sense for that object (this is
called dynamic typing).

##### Built-in Data types

Python\'s built-in (or standard) data types can be grouped into several
classes. Sticking to the hierarchy scheme used in the official Python
documentation these are **numeric types, sequences, sets and mappings**
(and a few more not discussed further here). Some of the types are only
available in certain versions of the language as noted below.

-   boolean: the type of the built-in values `True` and `False`. Useful
    in conditional expressions, and anywhere else you want to represent
    the truth or falsity of some condition. Mostly interchangeable with
    the integers 1 and 0. In fact, conditional expressions will accept
    values of any type, treating special ones like boolean `False`,
    integer 0 and the empty string `""` as equivalent to `False`, and
    all other values as equivalent to `True`.

Numeric types:

-   int: Integers; equivalent to C longs in Python 2.x, non-limited
    length in Python 3.x
-   long: Long integers of non-limited length; exists only in Python 2.x
-   float: Floating-Point numbers, equivalent to C doubles
-   complex: Complex Numbers

Sequences:

-   str: String; represented as a sequence of 8-bit characters in Python
    2.x, but as a sequence of Unicode characters (in the range of
    U+0000 - U+10FFFF) in Python 3.x
-   bytes: a sequence of integers in the range of 0-255; only available
    in Python 3.x
-   byte array: like bytes, but mutable (see below); only available in
    Python 3.x
-   list
-   tuple

Sets:

-   set: an unordered collection of unique objects; available as a
    standard type since Python 2.6
-   frozen set: like set, but immutable (see below); available as a
    standard type since Python 2.6

Mappings:

-   dict: Python dictionaries, also called hashmaps or associative
    arrays, which means that an element of the list is associated with a
    definition, rather like a Map in Java\

Some others, such as type and callables

##### Mutable vs Immutable Objects

In general, data types in Python can be distinguished based on whether
objects of the type are mutable or immutable. The content of objects of
immutable types cannot be changed after they are created.

+-------------------------+----------------------+
| Some *immutable* types  | Some *mutable* types |
+=========================+======================+
| -   int, float, complex | -   array            |
| -   str                 | -   bytearray        |
| -   bytes               | -   list             |
| -   tuple               | -   set              |
| -   frozenset           | -   dict             |
| -   bool                |                      |
+-------------------------+----------------------+

Only mutable objects support methods that change the object in place,
such as reassignment of a sequence slice, which will work for lists, but
raise an error for tuples and strings.

It is important to understand that variables in Python are really just
references to objects in memory. If you assign an object to a variable
as below,

``` python
a = 1
s = 'abc'
l = ['a string', 456, ('a', 'tuple', 'inside', 'a', 'list')]
```

all you really do is make this variable (`a`, `s`, or `l`) point to the
object (`1`, `'abc'`,
`['a string', 456, ('a', 'tuple', 'inside', 'a', 'list')]`), which is
kept somewhere in memory, as a convenient way of accessing it. If you
reassign a variable as below

``` python
a = 7
s = 'xyz'
l = ['a simpler list', 99, 10]
```

you make the variable point to a different object (newly created ones in
our examples). As stated above, only mutable objects can be changed in
place (`l[0] = 1` is ok in our example, but `s[0] = 'a'` raises an
error). This becomes tricky, when an operation is not explicitly asking
for a change to happen in place, as is the case for the `+=` (increment)
operator, for example. When used on an immutable object (as in `a += 1`
or in `s += 'qwertz'`), Python will silently create a new object and
make the variable point to it. However, when used on a mutable object
(as in `l += [1,2,3]`), the object pointed to by the variable will be
changed in place. While in most situations, you do not have to know
about this different behavior, it is of relevance when several variables
are pointing to the same object. In our example, assume you set `p = s`
and `m = l`, then `s += 'etc'` and `l += [9,8,7]`. This will change `s`
and leave `p` unaffected, but will change both `m` and `l` since both
point to the same list object. Python\'s built-in `id()` function, which
returns a unique object identifier for a given variable name, can be
used to trace what is happening under the hood.\
Typically, this behavior of Python causes confusion in functions. As an
illustration, consider this code:

``` python
def append_to_sequence (myseq):
    myseq += (9,9,9)
    return myseq

tuple1 = (1,2,3) # tuples are immutable
list1 = [1,2,3] # lists are mutable

tuple2 = append_to_sequence(tuple1)
list2 = append_to_sequence(list1)

print('tuple1 = ', tuple1) # outputs (1, 2, 3)
print('tuple2 = ', tuple2) # outputs (1, 2, 3, 9, 9, 9)
print('list1 = ', list1) # outputs [1, 2, 3, 9, 9, 9]
print('list2 = ', list2) # outputs [1, 2, 3, 9, 9, 9]
```

This will give the above indicated, and usually unintended, output.
`myseq` is a local variable of the `append_to_sequence` function, but
when this function gets called, `myseq` will nevertheless point to the
same object as the variable that we pass in (`t` or `l` in our example).
If that object is immutable (like a tuple), there is no problem. The +=
operator will cause the creation of a new tuple, and `myseq` will be set
to point to it. However, if we pass in a reference to a mutable object,
that object will be manipulated in place (so `myseq` and `l`, in our
case, end up pointing to the same list object).

Links:

-   3.1. Objects, values and
    types,
    The Python Language Reference, docs.python.org
-   5.6.4. Mutable Sequence
    Types,
    The Python Standard Library, docs.python.org

##### Creating Objects of Defined Types

Literal integers can be entered in three ways:

-   decimal numbers can be entered directly
-   hexadecimal numbers can be entered by prepending a 0x or 0X (0xff is
    hex FF, or 255 in decimal)
-   the format of octal literals depends on the version of Python:

:\* Python 2.x: octals can be entered by prepending a 0 (0732 is octal
732, or 474 in decimal)

:\* Python 3.x: octals can be entered by prepending a 0o or 0O (0o732 is
octal 732, or 474 in decimal)

Floating point numbers can be entered directly.

Long integers are entered either directly
(1234567891011121314151617181920 is a long integer) or by appending an L
(0L is a long integer). Computations involving short integers that
overflow are automatically turned into long integers.

Complex numbers are entered by adding a real number and an imaginary
one, which is entered by appending a j (i.e. 10+5j is a complex number.
So is 10j). Note that j by itself does not constitute a number. If this
is desired, use 1j.

Strings can be either single or triple quoted strings. The difference is
in the starting and ending delimiters, and in that single quoted strings
cannot span more than one line. Single quoted strings are entered by
entering either a single quote (\') or a double quote (\") followed by
its match. So therefore

`'foo' works, and`\
`"moo" works as well,`\
`     but`\
`'bar" does not work, and`\
`"baz' does not work either.`\
`"quux'' is right out.`

Triple quoted strings are like single quoted strings, but can span more
than one line. Their starting and ending delimiters must also match.
They are entered with three consecutive single or double quotes, so

`'''foo''' works, and`\
`"""moo""" works as well,`\
`     but`\
`'"'bar'"' does not work, and`\
`"""baz''' does not work either.`\
`'"'quux"'" is right out.`

Tuples are entered in parentheses, with commas between the entries:

``` python
(10, 'Mary had a little lamb')
```

Also, the parenthesis can be left out when it\'s not ambiguous to do so:

``` python
10, 'whose fleece was as white as snow'
```

Note that one-element tuples can be entered by surrounding the entry
with parentheses and adding a comma like so:

``` python
('this is a singleton tuple',)
```

Lists are similar, but with brackets:

``` python
['abc', 1,2,3]
```

Dicts are created by surrounding with curly braces a list of key/value
pairs separated from each other by a colon and from the other entries
with commas:

``` python
{ 'hello': 'world', 'weight': 'African or European?' }
```

Any of these composite types can contain any other, to any depth:

``` python
((((((((('bob',),['Mary', 'had', 'a', 'little', 'lamb']), { 'hello' : 'world' } ),),),),),),)
```

## Null object

The Python analogue of null pointer known from other programming
languages is *None*. *None* is not a null pointer or a null reference
but an actual object of which there is only one instance. One of the
uses of *None* is in default argument values of functions, for which see
../Functions#Default_Argument_Values.
Comparisons to *None* are usually made using *is* rather than ==.

Testing for None and assignment:

``` python
if item is None:
  ...
  another = None

if not item is None:
  ...

if item is not None: # Also possible
  ...
```

Using None in a default argument value:

``` python
def log(message, type = None):
  ...
```

PEP8 states that \"Comparisons to singletons like None should always be
done with is or is not, never the equality operators.\" Therefore, \"if
item == None:\" is inadvisable. A class can redefine the equality
operator (==) such that instances of it will equal None.

You can verify that None is an object by dir(None) or id(None).

See also ../Operators/#Identity
chapter.

Links:

-   4. Built-in
    Constants,
    docs.python.org
-   3.11.7 The Null
    Object,
    docs.python.org
-   Python None comparison: should I use "is" or
    ==?,
    stackoverflow.com
-   PEP 0008 \-- Style Guide for Python
    Code, python.org

## Type conversion

Type conversion in Python by example:

``` Python
v1 = int(2.7) # 2
v2 = int(-3.9) # -3
v3 = int("2") # 2
v4 = int("11", 16) # 17, base 16
v5 = long(2) # Python 2.x only, not Python 3.x
v6 = float(2) # 2.0
v7 = float("2.7") # 2.7
v8 = float("2.7E-2") # 0.027
v9 = float(False) # 0.0
vA = float(True) # 1.0
vB = str(4.5) # "4.5"
vC = str([1, 3, 5]) # "[1, 3, 5]"
vD = bool(0) # False; bool fn since Python 2.2.1
vE = bool(3) # True
vF = bool([]) # False - empty list
vG = bool([False]) # True - non-empty list
vH = bool({}) # False - empty dict; same for empty tuple
vI = bool("") # False - empty string
vJ = bool(" ") # True - non-empty string
vK = bool(None) # False
vL = bool(len) # True
vM = set([1, 2])
vN = set((1, 2)) # Converts any sequence, not just a list
vO = set("abc") # {'c', 'b', 'a'}
vP = set(b"abc") # {97, 98, 99}
vQ = list(vM)
vR = list({1: "a", 2: "b"}) # dict -> list of keys
vS = tuple(vQ)
vT = list("abc") # ['a', 'b', 'c']
print(v1, v2, v3, type(v1), type(v2), type(v3))
```

Implicit type conversion:

``` Python
int1 = 4
float1 = int1 + 2.1 # 4 converted to float
# str1 = "My int:" + int1 # Error: no implicit type conversion from int to string
str1 = "My int:" + str(int1)
int2 = 4 + True # 5: bool is implicitly converted to int
float2 = 4.5 + True # 5.5: True is converted to 1, which is converted to 1.0
```

Keywords: type casting.

Links:

-   2. Built-in Functions \#
    bool,
    docs.python.org
-   2. Built-in Functions \#
    list,
    docs.python.org
-   2. Built-in Functions \#
    float,
    docs.python.org
-   2. Built-in Functions \#
    int,
    docs.python.org
-   2. Built-in Functions \#
    set,
    docs.python.org
-   2. Built-in Functions \#
    str,
    docs.python.org
-   2. Built-in Functions \#
    type,
    docs.python.org
-   2. Built-in Functions \#
    tuple,
    docs.python.org

## Exercises

1.  Write a program that instantiates a single object, adds \[1,2\] to
    the object, and returns the result.
    1.  Find an object that returns an output of the same length (if one
        exists?).
    2.  Find an object that returns an output length 2 greater than it
        started.
    3.  Find an object that causes an error.
2.  Find two data types X and Y such that X = X + Y will cause an error,
    but X += Y will not.

