XClose

COMP0233: Research Software Engineering With Python

Home
Menu

Types

We have so far encountered several different 'types' of Python object:

  • integer numbers, for example 42,
  • real numbers, for example 3.14,
  • strings, for example "abc",
  • functions, for example print,
  • the special 'null'-value None.

The built-in function type when passed a single argument will return the type of the argument object. For example

In [1]:
type(42)
Out[1]:
int
In [2]:
type("abc")
Out[2]:
str

Converting between types

The Python type names such as int (integer numbers) and str (strings) can be used like functions to construct instances of the type, typically by passing an object of another type which can be converted to the type being called. For example we can use int to convert a string of digits to an integer

In [3]:
int("123")
Out[3]:
123
In [4]:
int("2") + int("2")
Out[4]:
4

or to convert a decimal number to an integer (in this case by truncating off the decimal part)

In [5]:
int(2.1)
Out[5]:
2

Conversely we can use str to convert numbers to strings

In [6]:
str(123)
Out[6]:
'123'
In [7]:
str(3.14)
Out[7]:
'3.14'

Object attributes and methods

Objects in Python have attributes: named values associated with the object and which are referenced to using the dot operator . followed by the attribute name, for example an_object.attribute would access (if it exists) the attribute named attribute of the object an_object. Each type has a set of pre-defined attributes.

Object attributes can reference arbitrary data associated with the object, or special functions termed methods which have access to both any argument passed to them and any other attributes of the object, and which are typically used to implement functionality connected to a particular object type.

For example the float type used by Python to represent non-integer numbers (more on this in a bit) has attribute real and imaginary which can be used to access the real and imaginary components when considering the value as a complex number

In [8]:
a_number = 12.5
print(a_number.real)
print(a_number.imag)
12.5
0.0

Objects of str type (strings) have methods upper and lower which both take no arguments, and respectively return the string in all upper case or all lower case characters

In [9]:
a_string = "Hello world!"
print(a_string.upper())
print(a_string.lower())
HELLO WORLD!
hello world!

If you try to access an attribute not defined for a particular type you will get an error

In [10]:
a_string.real
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[10], line 1
----> 1 a_string.real

AttributeError: 'str' object has no attribute 'real'
In [11]:
a_number.upper()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[11], line 1
----> 1 a_number.upper()

AttributeError: 'float' object has no attribute 'upper'

We can list all of the attributes of an object by passing the object to the built-in dir function, for example

In [12]:
print(dir(a_string))
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'removeprefix', 'removesuffix', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

The attributes with names beginning and ending in double underscores __ are special methods that implement the functionality associated with applying operators (and certain built-in functions) to objects of that type, and are generally not accessed directly.

In Jupyter notebooks we can also view an objects properties using tab-completion by typing the object name followed by a dot . then hitting tab

Operators

Now that we know more about types, functions and methods we should look again at what happens when we write:

In [13]:
four = 2 + 2

The addition symbol + here is an example of what is termed an operator, in particular + is a binary operator as it applies to pairs of values.

We can think of the above code as equivalent to something like

four = add(2, 2)

where add is the name of a function which takes two arguments and returns their sum.

In Python, these functions do exist, but they're actually methods of the first input: they're the mysterious double-underscore __ surrounded attributes we saw previously. For example the addition operator + is associated with a special method __add__

In [14]:
two = 2
two.__add__(two)
Out[14]:
4

The meaning of an operator varies for different types. For example for strings the addition operator + implements string concatenation (joining).

In [15]:
"Hello" + " " + "world"
Out[15]:
'Hello world'
In [16]:
"2" + "2"
Out[16]:
'22'

Sometimes we get an error when a type doesn't have an operator:

In [17]:
"Hello" - "world"
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[17], line 1
----> 1 "Hello" - "world"

TypeError: unsupported operand type(s) for -: 'str' and 'str'

The word "operand" means "thing that an operator operates on"!

Or when two types can't work together with an operator:

In [18]:
"2" + 5
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[18], line 1
----> 1 "2" + 5

TypeError: can only concatenate str (not "int") to str

Just as in mathematics, operators in Python have a built-in precedence, with brackets used to force an order of operations:

In [19]:
print(2 + 3 * 4)
14
In [20]:
print((2 + 3) * 4)
20

Supplementary material: Python operator precedence

Floats and integers

Python has two core numeric types, int for integers, and float for real numbers.

In [21]:
integer_one = 1
integer_ten = 10
float_one = 1.0
float_ten = 10.

Binary arithmetic operators applied to objects of float and int types will return a float

In [22]:
integer_one * float_ten
Out[22]:
10.0
In [23]:
float_one + integer_one
Out[23]:
2.0

In Python there are two division operators / and // which implement different mathematical operations. The true division (or just division) operator / implements what we usually think of by division, such that for two float values x and y z = x / y is another float values such that y * z is (to within machine precision) equal to x. The floor division operator // instead implements the operation of dividing and rounding down to the nearest integer.

In [24]:
float_one / float_ten
Out[24]:
0.1
In [25]:
float_one / integer_ten
Out[25]:
0.1
In [26]:
float_one // float_ten
Out[26]:
0.0
In [27]:
integer_one // integer_ten
Out[27]:
0
In [28]:
integer_one // float_ten
Out[28]:
0.0

In reality the float type does not exactly represent real numbers (as being able to represent all real numbers to arbitrary precision is impossible in a object with uses a finite amount of memory) but instead represents real-numbers as a finite-precision 'floating-point' approximation. This has many important implications for the implementation of numerical algorithms. We will not have time to cover this topic here but the following resources can be used to learn more for those who are interested.

Supplementary material:

Strings

Python built-in string type, supports many useful operators and methods. As we have seen already the addition operator can be used to concatenate strings

In [29]:
given = "Grace"
family = "Hopper"
full = given + " " + family

The multiplication operator * can be used to repeat strings

In [30]:
"Badger " * 3
Out[30]:
'Badger Badger Badger '

Methods such as upper and lower can be used to alter the case of the string characters.

In [31]:
print(full.lower())
print(full.upper())
grace hopper
GRACE HOPPER

The replace method can be used to replace characters

In [32]:
full.replace("c", "p")
Out[32]:
'Grape Hopper'

The count method can be used to count the occurences of particular characters in the string

In [33]:
full.count("p")
Out[33]:
2

We can use strip to remove extraneous whitespace from the start and end of a string:

In [34]:
"    Hello  ".strip()
Out[34]:
'Hello'