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 edit

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 edit

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,

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

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:

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:

Creating Objects of Defined Types edit

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:

(10, 'Mary had a little lamb')

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

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:

('this is a singleton tuple',)

Lists are similar, but with brackets:

['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:

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

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

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

Null object edit

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#Default_Argument_Values. Comparisons to None are usually made using is rather than ==.

Testing for None and assignment:

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:

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:

Type conversion edit

Type conversion in Python by example:

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:

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:

Exercises edit

  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.