# Python Programming/Data Types

| Previous: Sequences | Index | Next: Numbers |
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 str bytes tuple frozenset bool | array bytearray list set dict |
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,
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
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:
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 zero ( 0 ) (0732 is octal 732, or 474 in decimal)
   * Python 3.x: octals can be entered by prepending a zero followed by the letter O (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
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
Tuples are entered in parentheses, with commas between the entries:
Also, the parenthesis can be left out when it's not ambiguous to do so:
Note that one-element tuples can be entered by surrounding the entry with parentheses and adding a comma like so:
Lists are similar, but with brackets:
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:
Any of these composite types can contain any other, to any depth:

## 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 Python Programming/Functions. Comparisons to None are usually made using is rather than ==.
Testing for None and assignment:
Using None in a default argument value:
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 Python Programming/Operators/ 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:
Implicit type conversion:
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

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