Variables

1. Introduction

Variables are a combination of an identifier (a name) and a value.

2. Python Variables

In Python, the 'type' of a variable does not have to be declared - it is effectively inferred by the value of the variable (if it needs to be).

In Python, identifiers such as variable names can be reassigned. A benefit of this is that APIs can be more stable (as there is no need to specify exactly what types of things are input and output from functions). A downside is that, it is easier to make mistakes and accidentally re-use an identifier with unintended consequences.

The four primitive variable types in Python are: Integers, Floats, Strings, and Booleans:

Integers are whole numbers and are unbounded in Python. The largest Integer is constrained by memory availability.

Floats are standard Double-precision Floating-point numbers - a special subset of fractions. These have a 'Maximum Value' and a 'Minimum Normal Value' (smallest number > 0) which can be ascertained from the sys module. There are Float values for positive and negative infinity. Consider and run the following code:

import sys

# Exploring Float max and min values.
max = sys.float_info.max
min = sys.float_info.min
print("max", max)
print("min", min)

# Are there any changes?
print("max + min", max + min)
print("max - min", max - min)
print("min + min", min + min)

# What number do we need to add to max to get inf?
max2 = max
d = 2
while max2 == max:
    max2 = max2 + d
    d *= 2
d = d / 2
print("d", d)
print("max + d/2", max + d/2)
print("max + d", max + d)

# Find a number bigger than min which is the same when min is
# added to it.
min2 = min * 10**16
min3 = min2 + min
if min3 > min2:
    print("min3 > min2")
print("min2", min2)

This code generates the following output:

max 1.7976931348623157e+308
min 2.2250738585072014e-308
max + min 1.7976931348623157e+308
max - min 1.7976931348623157e+308
min + min 4.450147717014403e-308
d 9.9792015476736e+291
max + d/2 1.7976931348623157e+308
max + d inf
min2 2.2250754194454158e-293

Float is very useful for approximate numerical calculations. Do keep in mind that Floating Point Arithmetic is approximate - so calculations that use it are not necessarily accurate.

The Python Standard Library has two modules that support more arithmetic accuracy: decimal; and, fractions.

Strings are essentially blocks of text (which are sequences of single character length Strings). Strings can be assigned using; double or single quotes, or the constructor function str (which can for example create a String from an Integer). Strings can be compared and checked for equality using the operator '=='. Consider and run the following code:

s = "A"
s2 = "a"
s3 = "B"
print(s == s)  # Prints True
print(s == s2) # Prints False (case sensitive)
print(s == s3) # Prints False
print(s > s2)  # Prints False
print(s < s2)  # Prints True
print(str(3))  # Prints 3

Booleans are 'True' or 'False' and also equate to '0' or '1'.

Consider and run the following code:

t = True
f = False
print("t == t", t == t)             # Prints True
print("f == f", f == f)             # Prints True
print("t == 1", t == 1)             # Prints True
print("f == 0", f == 0)             # Prints True
print("t != f", t != f)             # Prints True
print("not t", not t)               # Prints False
print("not f", not f)               # Prints True
print("t and f", t and f)           # Prints False
print("t and not f", t and not f)   # Prints True
print("t or f", t or f)             # Prints True
print("not (t or f)", not (t or f)) # Prints False

More complex objects have state given by multiple primitives variables. Objects can also be comprised of multiple other objects as defined by a class.

The type of any object can be got using the function type from the builtins module. Consider and run the following code that initialises a variable named 'x' with the Integer value '1', then prints out the type of the variable 'x', then reassigns 'x' to have a String value, then again prints out the type of the variable 'x'.

# Set x = 1 and print the type of x")
x = 1
print(type(x)) # Prints <class 'int'>
x = "1"
print(type(x)) # Prints <class 'str'>

The output from running this code is:

<class 'int'>
<class 'str'>

3. Functions, Variable Scope and the Global Keyword

When passed as parameters into functions, variables are shared this is technically known as 'Call By Sharing'. If a variable is a primitive object then effectively a new variable is created within the function which is distinct from the variable outwith the function but that has the same value which is technically known as 'Call By Value'.

The scope of a variable is from where in the code it can be accessed. The keyword 'global' can modify the scope.

Consider the following:

x = 1
if x == 1:
    y = 2
print(y) # Prints 2

The code in the if statement is executed as the condition 'x == 1' evaluates as 'True'. If the condition were to evaluate as 'False', then the interpreter would skip to the end of the if statement, as happens running the following code:

x = 1
if x == 0:
    y = 2
print(y)

Which results in:

NameError: name 'y' is not defined

This is because 'y' does not get initialised and so cannot be passed into the print function.

Consider the following code:

x = 1
def my_function():
    x = 2
    print("b", x)
print("a", x)
my_function()
print("c", x)

That outputs:

a 1
b 2
c 1

At line 2, the interpreter reads a function definition for a function called my_function(), then skips to the end of the function and executes line 5. Then at line 6 my_function() is called. The interpreter then effectively goes back to line 2 and runs the function commands (lines 2 to 4). Then, at the end of the function, the interpreter returns to line 6 and moves directly on to line 7.

In this example, the variable inside the function called x is a different variable to the variable called x that is referred to outside of the function (even though the names are the same). Using the keyword 'global', we can make these variables the same as follows:

x = 1
def my_function():
    global x
    x = 2
    print("b", x)
print("a", x)
my_function()
print("c", x)

The output should be:

a 1
b 2
c 2

4. Deleting variables

Variables can be deleted using the keyword 'del' followed by the name of the variable or a tuple of variables to delete.

Deleting variables can free up memory for other things. The following code snippets shows how to initialise and delete variables:

a = 1
del a # <-- deletes a
#print(a) # <-- Would result in: NameError: name 'a' is not defined.
b = 2
c = 3
del(b, c) # <-- Deletes b and c.