Python Atoms: Booleans, Numbers, and Strings

In Python, we have three elementary data types: numbers, booleans, and strings. I will discuss each of them in turn.

1 Booleans

A boolean can have only two values: True and False.

type(False)
bool
type(True)
bool

Hint: Never use False or True as variable names. This is all I say about booleans for now but they – together with the operators for logical comparisons – will be important for many of the code structures later on.

2 Numbers

2.1 Integers

Python has two classes of numbers: integers and floating point numbers.

type(7)
int
type(7.0)
float

Any sequence of numbers without a dot specifies an integer. These can be either positive or negative.

+10
10
-10
-10

In principle, we can use Python just as an overqualified calculator.

5+5
10
5-5
0
5*5
25
5/5
1.0

Note that although we divided an integer by an integer, the result is a float here.

6//5
1
7%3
1
2**2
4
(2**2) + (4/3) + 1
6.333333333333333

Notice that if you want to change the precedence of operations you have to use parentheses.

7 / 5 + 2
3.4
7 / (5 + 2)
1.0

What happens if we divide by zero?

5/0
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
/tmp/ipykernel_2268/2874912419.py in <module>
----> 1 5/0

ZeroDivisionError: division by zero

Python notices that this is not a valid operation and throws an error.

Intermezzo: Variables

Of course we cannot just use Python as a naive calculator. We can also assign numbers to variables and perform calculations with them.

a = 7
b = 5
a / b
1.4

Let’s say we want to store the result of our division in another variable. We can easily do that.

a = 7
b = 5
c = a / b

This time you will not see the actual result. We have just assigned the division to the variable c but nothing in our code says that this result should be printed to the display as well. For that we the print function.

a = 7
b = 5
c = a / b
print(c)
1.4

If you want to change the value of a variable you can simply reassign it.

a = 7
print(a)
a = 5
print(a)
7
5

We can also use recursion in our variable assignments. Let’s say we want to decrease the old value of a by one.

a = 5
print(a)
a = a - 1
print(a)
5
4

We can write this more compactly as follows.

a = 5 
a -= 1
print(a)
4

You can use the same shortcut with other arithmetic operators.

a = 5
a *= 5
print(a)
25

2.2 Floats

OK, so floating-point numbers are the ones that have decimal points. You can use all of the previous operations.

2.0 + 3.0 
5.0

Notice that the result is also a float.

Since this subsection would otherwise be very short, let me take the opportunity to show you some more math functions. You can of course also use them with integers. For this we need to import the math module. We will explain modules later in more detail but for the moment you only need to know that a module brings you new functions you would otherwise not have.

import math

This module gives you some predefined constants such pi and e.

math.pi
3.141592653589793
math.e
2.718281828459045

But the main benefit are the additional functions.

# absolute values
math.fabs(-12.0)
12.0
# rounding down
math.floor(13.5)
13
# rounding up
math.ceil(13.5)
14
# logarithm with base e
math.log(1.0)
0.0
# logarithm with base 2
math.log(8.0, 2.0)
3.0
# square root
math.sqrt(4.0)
2.0

So, that’s it for now. But there is more and you can look it up by browsing through the help file for the module.

help(math)
Help on module math:

NAME
    math

MODULE REFERENCE
    https://docs.python.org/3.6/library/math
    
    The following documentation is automatically generated from the Python
    source files.  It may be incomplete, incorrect or include features that
    are considered implementation detail and may vary between Python
    implementations.  When in doubt, consult the module reference at the
    location listed above.

DESCRIPTION
    This module is always available.  It provides access to the
    mathematical functions defined by the C standard.

FUNCTIONS
    acos(...)
        acos(x)
        
        Return the arc cosine (measured in radians) of x.
    
    acosh(...)
        acosh(x)
        
        Return the inverse hyperbolic cosine of x.
    
    asin(...)
        asin(x)
        
        Return the arc sine (measured in radians) of x.
    
    asinh(...)
        asinh(x)
        
        Return the inverse hyperbolic sine of x.
    
    atan(...)
        atan(x)
        
        Return the arc tangent (measured in radians) of x.
    
    atan2(...)
        atan2(y, x)
        
        Return the arc tangent (measured in radians) of y/x.
        Unlike atan(y/x), the signs of both x and y are considered.
    
    atanh(...)
        atanh(x)
        
        Return the inverse hyperbolic tangent of x.
    
    ceil(...)
        ceil(x)
        
        Return the ceiling of x as an Integral.
        This is the smallest integer >= x.
    
    copysign(...)
        copysign(x, y)
        
        Return a float with the magnitude (absolute value) of x but the sign 
        of y. On platforms that support signed zeros, copysign(1.0, -0.0) 
        returns -1.0.
    
    cos(...)
        cos(x)
        
        Return the cosine of x (measured in radians).
    
    cosh(...)
        cosh(x)
        
        Return the hyperbolic cosine of x.
    
    degrees(...)
        degrees(x)
        
        Convert angle x from radians to degrees.
    
    erf(...)
        erf(x)
        
        Error function at x.
    
    erfc(...)
        erfc(x)
        
        Complementary error function at x.
    
    exp(...)
        exp(x)
        
        Return e raised to the power of x.
    
    expm1(...)
        expm1(x)
        
        Return exp(x)-1.
        This function avoids the loss of precision involved in the direct evaluation of exp(x)-1 for small x.
    
    fabs(...)
        fabs(x)
        
        Return the absolute value of the float x.
    
    factorial(...)
        factorial(x) -> Integral
        
        Find x!. Raise a ValueError if x is negative or non-integral.
    
    floor(...)
        floor(x)
        
        Return the floor of x as an Integral.
        This is the largest integer <= x.
    
    fmod(...)
        fmod(x, y)
        
        Return fmod(x, y), according to platform C.  x % y may differ.
    
    frexp(...)
        frexp(x)
        
        Return the mantissa and exponent of x, as pair (m, e).
        m is a float and e is an int, such that x = m * 2.**e.
        If x is 0, m and e are both 0.  Else 0.5 <= abs(m) < 1.0.
    
    fsum(...)
        fsum(iterable)
        
        Return an accurate floating point sum of values in the iterable.
        Assumes IEEE-754 floating point arithmetic.
    
    gamma(...)
        gamma(x)
        
        Gamma function at x.
    
    gcd(...)
        gcd(x, y) -> int
        greatest common divisor of x and y
    
    hypot(...)
        hypot(x, y)
        
        Return the Euclidean distance, sqrt(x*x + y*y).
    
    isclose(...)
        isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0) -> bool
        
        Determine whether two floating point numbers are close in value.
        
           rel_tol
               maximum difference for being considered "close", relative to the
               magnitude of the input values
            abs_tol
               maximum difference for being considered "close", regardless of the
               magnitude of the input values
        
        Return True if a is close in value to b, and False otherwise.
        
        For the values to be considered close, the difference between them
        must be smaller than at least one of the tolerances.
        
        -inf, inf and NaN behave similarly to the IEEE 754 Standard.  That
        is, NaN is not close to anything, even itself.  inf and -inf are
        only close to themselves.
    
    isfinite(...)
        isfinite(x) -> bool
        
        Return True if x is neither an infinity nor a NaN, and False otherwise.
    
    isinf(...)
        isinf(x) -> bool
        
        Return True if x is a positive or negative infinity, and False otherwise.
    
    isnan(...)
        isnan(x) -> bool
        
        Return True if x is a NaN (not a number), and False otherwise.
    
    ldexp(...)
        ldexp(x, i)
        
        Return x * (2**i).
    
    lgamma(...)
        lgamma(x)
        
        Natural logarithm of absolute value of Gamma function at x.
    
    log(...)
        log(x[, base])
        
        Return the logarithm of x to the given base.
        If the base not specified, returns the natural logarithm (base e) of x.
    
    log10(...)
        log10(x)
        
        Return the base 10 logarithm of x.
    
    log1p(...)
        log1p(x)
        
        Return the natural logarithm of 1+x (base e).
        The result is computed in a way which is accurate for x near zero.
    
    log2(...)
        log2(x)
        
        Return the base 2 logarithm of x.
    
    modf(...)
        modf(x)
        
        Return the fractional and integer parts of x.  Both results carry the sign
        of x and are floats.
    
    pow(...)
        pow(x, y)
        
        Return x**y (x to the power of y).
    
    radians(...)
        radians(x)
        
        Convert angle x from degrees to radians.
    
    sin(...)
        sin(x)
        
        Return the sine of x (measured in radians).
    
    sinh(...)
        sinh(x)
        
        Return the hyperbolic sine of x.
    
    sqrt(...)
        sqrt(x)
        
        Return the square root of x.
    
    tan(...)
        tan(x)
        
        Return the tangent of x (measured in radians).
    
    tanh(...)
        tanh(x)
        
        Return the hyperbolic tangent of x.
    
    trunc(...)
        trunc(x:Real) -> Integral
        
        Truncates x to the nearest Integral toward 0. Uses the __trunc__ magic method.

DATA
    e = 2.718281828459045
    inf = inf
    nan = nan
    pi = 3.141592653589793
    tau = 6.283185307179586

FILE
    /anaconda/lib/python3.6/lib-dynload/math.cpython-36m-darwin.so

Another thing that you might have noticed, is that I use the # to place comments. In ipython you can also use markdown to comment your code but for more complicated projects you will probably want to switch to a serious text and then commenting with # comes in handy.

2.3 Type conversions

What if you mix different types? Sometimes Python will just try to help you.

True + False - 1
0

Python implicitly converts the booleans True and False to 1 and 0 respectively. In a similar way, Python also converts integers to floating point numbers when you do computations with them.

4 + 12.0
16.0

You can also do type conversions explicitly by using the int, float, and bool functions.

float(1)
1.0
int(2.7)
2
int(-2.7)
-2
float(False)
0.0
bool(1)
True

By the way, what happens if you convert a number different from 0 or 1 to boolean? Let’s find out.

bool(2.3)
True

Every number that is not 0 gets evaluated as True when converted to a boolean. Similarly, an empty string gets converted to False but every non-empty string is evaluated as True. This also holds for lists etc.

Finally, this explicit conversion works also with strings which you don’t know yet but will soon.

float('1.6')
1.6
float('1e4')
10000.0

3 Strings

3.1 Definition

In Python (and real life) we frequently not only work with numbers but also with text. Such text data can be stored in strings, the last basic data type we will get to know. A string is created by using either single or double quotes.

'Hello World!'
'Hello World!'
"Hello World!"
'Hello World!'
type('Hello World!')
str

The main purpose of being able to choose between single or double quotes is to faciliate entering strings with quotes in them.

"Keynes said: "In the long run we are all dead""
  File "<ipython-input-49-ac43e2d97d42>", line 1
    "Keynes said: "In the long run we are all dead""
                    ^
SyntaxError: invalid syntax
'Keynes said: "In the long run we are all dead."'
'Keynes said: "In the long run we are all dead."'

Of course, we can again store strings in variables.

shout_out = 'Hello World'

Again, this time nothing is printed out. To see something on our scree, we have to use the print function.

print(shout_out)
Hello World

Note that there is a difference between the echoing of the string and the print function.

'Keynes said: "In the long we are all dead".'
'Keynes said: "In the long we are all dead".'
print('Keynes said "In the long we are all dead".')
Keynes said "In the long we are all dead".

The print function is helpful to make strings more readible and combine them easily.

print('The current', 'population', 'is', 2, 'and they hate each other.')
The current population is 2 and they hate each other.

The function creates a whitespace between the different strings, automatically converts numbers to strings and adds a newline at the end. Again, you can also explicitly force a type conversion using the str function.

str(88.0)
'88.0'
str(True)
'True'

One more thing here. When you convert an empty string to a boolean, it is evaluated as `False’.

bool('')
False

This might seem weird but will be very helpful later on.

3.2 Escape

You can use a backslash to escape the normal meaning of a character when you create strings. Two very common uses for the escape are \n and \t for a new line or a tab.

print('What did \nKeynes say?')
What did 
Keynes say?
print('What did \tKeynes say?')
What did 	Keynes say?

You can also use the backslash to escape the normal meaning of single or double quotes.

print('Keynes said \"In the long we are all dead.\"')
Keynes said "In the long we are all dead."

For an actual backslash you need to write two backslashes.

print('This is a backslash: \\.')
This is a backslash: \.

3.3 Combining and duplicating with + and *

When you use the operators + and * with strings they function differently. Of course, we can’t add and substract strings!

'Keynes ' + 'knew ' + 'Wittgenstein.' 
'Keynes knew Wittgenstein.'
'Who said that? ' + 'Keynes '*3 + '!'
'Who said that? Keynes Keynes Keynes !'

As you can see, + can be used to join strings, while * can be used to duplicate strings.

3.4 Extracting and slicing strings

Extracting specific characters or substrings from strings is very easy. Let us first extract characters.

economist = 'Friedrich August von Hayek'

Let’s say we want to get the first character of this string. A natural way to do this would be the following.

economist[1]
'r'

This is clearly wrong! To obtain the character you have to specify the offset which is not 1 but 0.

economist[0]
'F'

Thus, to get the fourth element you write.

economist[3]
'e'

By using negative numbers you can start from the other side of the string.

economist[-1]
'k'

Let’s say you want to change the third element of a string to an ‘e’. You might think you can do this as follows.

economist[2] = 'e'
economist
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-70-91dfe36e3ca4> in <module>()
----> 1 economist[2] = 'e'
      2 economist

TypeError: 'str' object does not support item assignment

We messed up again! The problem is due to the fact that you can’t change a string because it is immutable: Once it is created you cannot change it. In fact, you have to create a new string. We will do this later.

Now, we have extracted single characters. What if we want to extract whole substrings? We can slice strings using [start : end : step].

# extract the whole string
economist[:]
'Friedrich August von Hayek'
# extract the string starting from a specific offset 
economist[17:]
'von Hayek'
# extracts the string from the beginning and stops at a specific offset minus 1
economist[:9]
'Friedrich'
# extracts the string from a specific offset and stops at a specific offset minus 1
economist[10:16]
'August'

Be careful with the index when slicing. The left index is always included while the right one is not. This is always a little bit tricky: For example, to get to the 4th letter which is at offset 3 you have to type 4 for slicing in order to catch it.

# extracts the string from a specific offset and stops at a specific offset in specific steps
economist[::2]
'FidihAgs o ae'

You can also again use negative indexes.

economist[-5:]
'Hayek'
# 'cool' trick to reverse strings
economist[-1::-1]
'keyaH nov tsuguA hcirdeirF'
# this can be shortened
economist[::-1]
'keyaH nov tsuguA hcirdeirF'

3.5 Length of a String with len

The length of a string can be found out with the len function.

economist = 'Rodrik'
len(economist)
6

3.6 Splitting and joining strings with the slit and join methods

Say you have a string and you want to unpack it into several substrings. This can be done with the split method. Since this function is specific to strings (unless for example +) it is used differently than the functions we have seen before.

economists = 'Hayek, Keynes, Rodrik, Marshall, Marx'
economists.split(',')
['Hayek', ' Keynes', ' Rodrik', ' Marshall', ' Marx']

Note that we could also have written this without creating a variable as follows.

'Hayek, Keynes, Rodrik, Marshall, Marx'.split(',')
['Hayek', ' Keynes', ' Rodrik', ' Marshall', ' Marx']

You will have noticed that we now have five different strings which are separated by commas and enclosed by squared brackets. By splitting the string, we have created a list, which is basically a container to store other objects, like several strings. We will come back to them later.

To get back from a list of strings to one string we can use the join function.

list_of_economists = ['Hayek', ' Keynes', ' Rodrik', ' Marshall', ' Marx']
', '.join(list_of_economists)
'Hayek,  Keynes,  Rodrik,  Marshall,  Marx'

This might look strange because you first specify the string that is used to combine the strings on the list. In this case it is ,.

3.7 Other string functions

Of course, there are also a bunch of other functions for strings. In the following, I show you some.

stupid = 'If you rearrange the letters in "ECONOMICS", you get "COMIC NOSE"'
# count occurences
stupid.count('you')
2
# check start of the string
stupid.startswith('If')
True
# check end of the string
stupid.endswith('NOSE')
False
# find offset of first occurence
stupid.find('you')
3
# find offset of last occurence
stupid.rfind('you')
45
# strip string of substrings at beginning or end
string = "'Economics'"
print(string)
'Economics'
print(string.strip("'"))
Economics

OK, finally, what if we want to replace certain parts of a string. As mentioned above, a simple reassignment did not work. We need the replace function, which as a default replaces the first occurence of a string.

assertion = 'Keynes is a fool.'
assertion.replace('Keynes', 'Hayek')
'Hayek is a fool.'

Note that this operation hasn’t changed our original string.

assertion
'Keynes is a fool.'

You already know that we cannot do that, since strings are immutable. The only thing we can do is to create a new string and reassign it to our old variable.

assertion = assertion.replace('Keynes', 'Hayek')
assertion
'Hayek is a fool.'

OK, that’s it so far with the atomic data types. What if we want to combine these types to more complicated structures? We will more types of objects which are discussed in the next section.

Sources

The structure of exposition was taken from Bill Lubanovic (2015): Introducing Python: Modern Computing in Simple Packages. O’Reilly: Sebastopol, CA.