Variables

Try me

Open In ColabBinder

Introduction

In the Hello world Notebook, we already provided an informal definition of variables as symbols that we introduce in our code to manage values that might change, for instance, depending on the use we want to make of the program. Thinking of programming languages as a proxy language between natural language and machine language, we can think of variables as human-readable names that we give to binary values stored in memory. As we know, computers work with binary data, and programming languages introduce variables to facilitate the management of these data.

Although experienced programmers exhibit extreme binary code interpretation skills, for most humans, binary code is just gibberish and messages between humans and machines could be lost in translation if programming languages did not introduce variables to facilitate the process, as this video illustrates:

Assignment

As mentioned in the introduction, in Python, we define a variable and assign a value using the ‘=’ operator, in what is called an assignment statement. To the left of the ‘=’ operator, we write the name of the variable, and to the right of the ‘=’ operator, we give the value that we want to assign to the variable, for instance:

[1]:
my_first_variable = 15
print("The value of my first variable is:", my_first_variable)
The value of my first variable is: 15

Learn the type of a variable (Type function)

If we want to learn the type of the variable, we can use the built-in function type() (see the docs).

Type function

The type function returns the type of the object that it is passed as argument. For instance, let us see what happens if we call the type function on the variable my_first_variable, it will return the string ‘int’.

[2]:
print("The type of my_first_variable is:", type(my_first_variable))
The type of my_first_variable is: <class 'int'>

Note that the type is <class 'int'>, which is the type of the integer numbers in Python.

Implicit versus Explicit Assignment

Note that, to be able to support the management of different types of information, we need to specify what is the type of variable we want to store, but in the previous code cells, we never did specify the type of variables explicitly. In some programming languages, like C or Java, the type of variables must be specified explicitly, meaning that we need to define the type in the assignment statement. In Python, as we will see, we do not need to specify the type explicitly, we can let Python infer the type of the variable from the value that we assign to it. We will see this in detail in the next sections, but for now, let us assume that we want to store a number in a variable. We can just write the number to the right of the ‘=’ operator, and Python will infer the type of the variable from the value that we assign to it.

However, we can also explicitly specify the type of the variable, in this case, we want to store a string in a variable. We can do this by writing the type of the variable in the assignment statement, using the built-in string() function (see the docs).

[3]:
my_number_string = str(10) # This is a string variable, using the str() function to convert the number 10 into a string.
print("The value of my number string is:", my_number_string)
print("The type of my number string is:", type(my_number_string))
The value of my number string is: 10
The type of my number string is: <class 'str'>

Note that the syntax in both examples is similar, and we use the same value (10), but the resulting type of the variable is different (in the latter script, the type is <class 'str'>. In the first example, we let Python infer the type of the variable from the value that we assign to it, and in the second example, we explicitly specify the type of the variable using the str function, which converts the argument into a string. Normally in Python, we will define the type of the variable implicitly, but in some cases we will specify it explicitly to avoid ambiguity and errors. For instance, say we want to build a program that asks the user to enter a number, and then prints the power of 2 of the provided number back to the user, we can use the int function to convert the result of the `input() function into an integer number, so that we can perform numeric operations:

[4]:
number = int(input("Please enter an integer number: "))
result = number * number # The asterisk is the multiplication operator
print("The power of 2 of", number, "is", result)
The power of 2 of 4 is 16

Had we not converted the result of the input() function into an integer number, we would have had an error, because we would have tried to perform a numeric operation on a string. Just try the same example without the int()function to see what happens!

Variables and Types

794b1693210c467496be44d84ca4eb79

Ok, so we know that, ultimately, the value of a variable is a binary value, so Python will need to encode the value of the variable into bits. The process of encoding the value will depend on the type of information that we want to store. For instance, for some application we might want our variable to represent the text “10”, but for other applications we might want to store the number 10 in decimal, and in other applications we might want to store the sequence of bits 10.

The most basic variable types in Python are Integer numbers, floating point numbers, strings (sequences of characters), and boolean. These basic types are known as primitive types. Moreover, the main types of data (and in extension of variables) are:

  • Numbers: Numbers represent data like integers, floating-point numbers, complex numbers, etc. with which we can perform arithmetic operations and math.

  • Strings: String are sequences of characters of any length, representing paragraphs, words, sentences, or characters.

  • Boolean: boolean values, either True or False, are used in boolean logic operations.

Additionally, in Python, we have other types of variables known as iterables, which are collections of values. Due to the increased complexity and importance of iterables, they are presented in another tutorial.

The following sections describe the main characteristics of these variables in Python, but before we dive into practice, let us pay some attention to the syntax of the assignment statement.

Numeric types

There are three basic numeric types in Python:

  • Integer (int): Integer numbers, i.e. numbers with no decimal part

  • Float (float): Numbers with fractional part

  • Complex (complex): Numbers with real and imaginary part

As mentioned above, the specific type of the variable is implicitly obtained from the assignment. As shown in this code cell, to define an integer variable, you just need to enter the integer value to the right of the ‘=’ operator. To define a float variable, you need to enter the float value to the right of the ‘=’ operator, using the ‘.’ (dot) character as decimal separator. And finally, to define a complex variable, you need to enter the complex value to the right of the ‘=’ operator, using the ‘j’ character to define the imaginary part and using the ‘+’ operator to define a variable with combining real and imaginary parts:

[5]:
# Integer variable
integer_var = 5
print("The value of integer_var is:")
print(integer_var)
print("The type of integer_var is:")
print(type(integer_var))

# Real variable
float_var = 5.2
print("The value of float_var is:")
print(float_var)
print("The type of float_var is:")
print(type(float_var))

#Commplex variable
complex_var = 1.5+2.6j
print("The value of complex_var is:")
print(complex_var)
print("The type of complex_var is:")
print(type(complex_var))


The value of integer_var is:
5
The type of integer_var is:
<class 'int'>
The value of float_var is:
5.2
The type of float_var is:
<class 'float'>
The value of complex_var is:
(1.5+2.6j)
The type of complex_var is:
<class 'complex'>

Built-in numeric type constructors

Also, there are built-in functions to define the type of a numeric variables explictly, as already shown before with the int type and the int() function, we can use the float function float() to explicitly define a float variable, and the complex function complex() to define a complex variable:

[6]:
# int creates an integer variable, from a number of string
int_var_2 = int("5")
print("The value of int_var_2 is:", int_var_2)
print("The type of int_var is:", type(int_var_2))

# float() creates a float variable from a number or string, if possible
float_var_2 = float(3)
print("The value of float ar is:", float_var_2)
print("The type of float_var_2 is:", type(float_var_2))

# complex() creates a complex number from real and imaginary floating numbers:
complex_var_2 = complex(5, 2.5)
print(complex_var_2)
print(type(complex_var_2))
The value of int_var_2 is: 5
The type of int_var is: <class 'int'>
The value of float ar is: 3.0
The type of float_var_2 is: <class 'float'>
(5+2.5j)
<class 'complex'>

Built-in functions

There are some interesting built-in functions that can be used with numeric variables. For instance:

Methods and attributes

We have not introduced yet the concepts of methods and attributes. We will cover this concepts more extensively in the object-oriented programming tutorial, but for now, let´s provide an informal definition of the concepts:

  • Methods: Methods are functions that are associated with the type of the object, and are called using the dot (.) operator on the variable. For instance, if we have a variable my_number of type float, we can call the as_integer_ratio() method on this variable, and the result will be a duple of numbers containing the numerator and denominator of a fraction of value equal to my_number.

  • Attributes: Attributes are variables that are associated with the type of the object, and are accessed using the dot (.) operator on the variable. For instance, if we have a variable my_number of type complex, we can access the imaginary part in the imag attribute on this variable.

The numeric built-in types provide some interesting built-in methods and attributes, shown in the following code cell:

[ ]:
float_var_3 = 1.5

# as_integer_ratio() provides a pair of positive numbers whose ratio is equal to original float number

print("The float value", float_var_3, "can be expressed as the ratio of", float_var_3.as_integer_ratio())

complex_var_3 = 2.5 + 5j

# conjugate() returns the complex conjugate of the original complex number
print("The conjugate of", complex_var_3, "is", complex_var_3.conjugate())

# imag returns the imaginary part of the original complex number
print(complex_var_3.imag)

# real returns the real imaginary part of the original complex number
print(complex_var_3.real)

Arithmetic operators and numeric variables

Basic arithmetic operators like addition, subtraction, multiplication, and division operators can be used with numeric variables in Python. The following table lists the arithmetic operators that can be used with numeric variables:

Operation

Symbol

Description

Addition

+

Adds the value of the operands

Subtraction

-

Subtracts the value of the operands

Multiplication

*

Multiplies the value of the operands

Division

/

Divides the value of the first operand by the second

Exponentiation

**

Raises the first operand to the power of the second

Floor division

//

Divides the first operand by the second and returns the integer part of the result

Remainder (Modulo)

%

Returns the remainder of the division of the first operand by the second

Use the following cell to test the arithmetic operators:

[ ]:
number = 1 + 2 * 3 / 4.0 - 0.5
print(number)

Important! Use parentheses to force the order of operations Python will use the order of operations to determine the order in which the operations are performed, so if you want to force the order of operations, please use parentheses.

[ ]:
number = (1 + 2) * 3 / 4.0 - 0.5
print(number)

I would like to call your attention on two operators you may not be familiar with. The remainder, or Modulo (%) operator returns the integer remainder of the division. Likewise, the integer division operator (//) returns the integer division or true integer division. If we are dealing with integers, the dividend is equal to the result of the integer division multiplied by the divisor, times the integer remainder:

[ ]:
dividend = 11
divisor = 3
remainder = dividend % divisor
print("Remainder of", dividend, "divided by", divisor, "is", remainder)
true_division = dividend // divisor
print("Integer division is", true_division)
print(true_division*divisor + remainder)

Extra tips: Hexadecimal and binary numbers

We can use the 0x prefix to define a hexadecimal number, and the 0b prefix to define a binary number. For instance, the hexadecimal number 0xFF is the same as the binary number 0b11111111:

[ ]:
hex_number = 0xFF
print(hex_number)
bin_number = 0b11111111
print(bin_number)

Extra tips: Bitwise binary operators

Python allows to perform bitwise (applied to every bit) operations to integer values. These operators work on the bits representing the number, rather than the number itself. The following table lists the bitwise operators that can be used with numeric variables:

Operation

Symbol

Description

Bitwise AND

&

Returns 1 bitwise if the bits of both operands are 1.

Bitwise OR

|

Returns 1 bitwise if at least one of the bits of both operands is 1.

Bitwise XOR

^

Returns 1 bitwise if the bits of both operands are different.

Bitwise NOT

~

Returns the complement of the bits of the operand.

Bitwise Left Shift

<<

Shifts the bits of the operand to the left.

Bitwise Right Shift

>>

Shifts the bits of the operand to the right.

[ ]:
a = 0b10
b = 0b11
print(a & b)
print(a | b)
print(a ^ b)
print(a << 2)
print(a >> 2)
print(~a)

Numeric cheatsheet

The following image shows a cheatsheet for numeric types in Python: Numeric cheatsheet

String (text) type

Text is stored in variables of type string. To define a text variable, just enclose the text in single quotes ('...') or double quotes ("..."):

[ ]:
s = 'one string'
print(s)
print("The type is", type(s))
s = "another string"
print(s)
s = "Don't worry about quotes"
print(s)

Special characters

Ok, so now we know that we need to enclose the text in single and double quotes, but what if what if we want to store a string that contains quotes? Quotes are not allowed inside a string, and in that sense, they are considered a special character. To deal with this, we can use the escape character (\) to escape the quote we want to include in our string:

[ ]:
s = "This is a \"special\" string"
print(s)

There are other special characters that can be used in strings. Special characters can be escaped with \. Find below some examples of special characters:

  • \n: new line

  • \t: tab space

  • \N{name}: Unicode character by character name eg (\N{ALIEN})

  • \uhhhh: Unicode character encoding (base 16) eg (\u1F47E)

Built-in functions and string variables

For now, we only want to highlight the built-in function len returns the length of a string:

[ ]:
text = "Hello world!"
print(len(text))

Built-in String type constructor

The function str() can be used to create string variables. It takes any numeric value as parameter, so it can be used to convert any value to a string:

[ ]:
complex_var = 2.5 + 5j
compex_var_str = str(complex_var_3)
print(compex_var_str)
print(type(compex_var_str))

String type built-in methods and attributes

String variables have many useful built-in methods to manipulate strings. You can read their description in the docs, but let´s highlight here some outstanding examples:

[ ]:
my_string = "Hello world!"
# Find the index of the first occurrence of the letter "o" in the string
print(my_string.index("o"))

# Count how many letters "l" are in the string
print(my_string.count('l'))

my_string = "this is a string"
# To upper case
print(my_string.upper())

# To lower case
print(my_string.lower())

# Or capitalize the first character of the string
print(my_string.capitalize())


# check if the string ends with the specified suffix
print(my_string.endswith("!"))

# Return a copy of the string with the specified substring replaced
print(my_string.replace("world", "Thomas"))

The format method is a very useful method to work with template strings. It can take an arbitrary number of input parameters and returns a string with the values of its parameters formatted according to the template string format:

[1]:
# format allows to insert the values of variables in a string using {}
my_var = 1
my_second_var = 2
my_third_var = 3

print('my_var is: {}, my_second_var is: {}, my_third_var is: {}'.format(my_var, my_second_var, my_third_var))

# You can access the parameters by position using their index

print('my_var is: {0}, my_second_var is: {1}, my_third_var is: {2}'.format(my_var, my_second_var, my_third_var))

print("my_var is {1}, and my_second_var is {0}".format(my_second_var, my_var))

# You can also use names for the parameters:

print("my_var is {first}, and my_second_var is {latest}".format(first=my_second_var, latest=my_var))

# format also supports different number formats (:d) decimal, :x hexadeximal, :o octal, :b binary:
print("int: {0:d};  hex: {0:x};  oct: {0:o};  bin: {0:b}".format(42))

# float numbers use the :f format. You can specify the number of decimals to show:
print("one decimal: {0:.1f}, three decimals: {0:.3f}".format(3.141516))

# You can also represent percentages with format:

print("The ratio is {0:.2%}".format(6.0/7.0))

# You can access the attributes of parameters:

print("The imaginary part is {0.imag:.2}".format(2.135-2.167j))
my_var is: 1, my_second_var is: 2, my_third_var is: 3
my_var is: 1, my_second_var is: 2, my_third_var is: 3
my_var is 1, and my_second_var is 2
my_var is 2, and my_second_var is 1
int: 42;  hex: 2a;  oct: 52;  bin: 101010
one decimal: 3.1, three decimals: 3.142
The ratio is 85.71%
The imaginary part is -2.2

Formatted strings

Now, with formatted strings, you can use the f prefix to create a string with the values of variables formatted according to the same template format string format as above, but taking the values of the variables directly from the context:

[2]:
my_var = 1
my_second_var = 2
my_third_var = 3

print(f'my_var is: {my_var}, my_second_var is: {my_second_var}, my_third_var is: {my_third_var}')

# format also supports different number formats (:d) decimal, :x hexadeximal, :o octal, :b binary:
int_num = 42
print(f"int: {int_num:d};  hex: {int_num:x};  oct: {int_num:o};  bin: {int_num:b}")

# float numbers use the :f format. You can specify the number of decimals to show:
float_num = 3.141516
print(f"one decimal: {float_num:.1f}, three decimals: {float_num:.3f}")

# You can also represent percentages with format:
percent_num = 6.0/7.0
print(f"The ratio is {percent_num:.2%}")

# You can access the attributes of parameters:
complex_num = 2.135-2.167j
print(f"The imaginary part is {complex_num.imag:.2}")
my_var is: 1, my_second_var is: 2, my_third_var is: 3
int: 42;  hex: 2a;  oct: 52;  bin: 101010
one decimal: 3.1, three decimals: 3.142
The ratio is 85.71%
The imaginary part is -2.2

Note that up to this point, we used the print function passing different values, but we can also use the format function to gain control and make our strings more readable.

Basic arithmetic operations with strings

The addition (+) operator is used to concatenate strings. It is also possible to use the multiplication operator with a string and an integer operand to repeat a string a number of times:

[ ]:
helloworld = "hello" + " " + "world"
print(helloworld)

lotsofhellos = "hello" * 10
print(lotsofhellos)

String cheatsheet

This cheatsheet contains a summary of the most important string operations in Python.

String cheatsheet

Boolean type

Boolean variables are used to store the values True or False, and they are quite useful to build the logic of our programs. The type of a boolean variable is bool and it defines if something is True or False.

[ ]:
right = True
print(right)
print(type(right))

wrong = False
print(wrong)

Logical operators

In the example above, it makes no practical sense to assign the value True to a variable, but rather to use logic to determine if a condition is met. In practice, we may assign some initial value to a boolean variable, and then we will use Logical operators to determine if different conditions are met and control the logic of our program. In short, logical operators are operators that either return boolean values or use boolean variables as operators. Logical operators are:

Operator

Symbol

Description

and

a & b

Returns True if both operands (boolean variables a and b) are True

or

a | b

Returns True if at least one of the operands (boolean variables a and b) is True

not

!a

Returns True if the operand (boolean variable a) is False

xor

a ^ b

Returns True if exactly one of the operands (boolean variables a and b) is True

Comparison operators

Comparison operators are used to compare two values and return a boolean value depending on the result of the comparison. Comparison operators are:

Operator

Symbol

Description

Greater than >

a > b

Returns True if the numeric operand a is greater than the numeric operand b

Equal to ==

a == b

Returns True if the numeric operand a is equal to the numeric operand b

Greater than or equal to >=

a >= b

Returns True if the numeric operand a is greater than or equal to the numeric operand b

Less than or equal to <=

a <= b

Returns True if the numeric operand a is less than or equal to the numeric operand b

Less than <

a < b

Returns True if the numeric operand a is less than the numeric operand b

Boolean cheatsheet

This cheatsheet contains a summary of the main features of boolean variables you should be aware of.

Boolean cheatsheet

None type

None is a special variable which is totally void, null or empty:

[ ]:
var = None

print(var)

Extra tips

Multiple assignment

We can define more than one variable in a single statement:

[ ]:
x, y = 50, 100
print(x, y)

Ternary assignment

Also, Python implements a ternary operator that allows to assign the value of the variable depending on a condition:

[ ]:
turn_right = True
next_turn = "right" if turn_right else "left"
print(next_turn)

How variables work in Python

You probably are wondering how the magic happens, how variables work in Python, how does Python manage internally the variables that you create in your code. The key concept here is that Python manages variables as references to objects. When you declare a variable and assign a value to it using the following happens:

  • Python creates a data structure called base structure where it will store the information of your variable.

  • It sets the type field of the base structure to the type of your variable

  • It sets the value field of the base structure to the value of your variable

  • It creates the name of the variable in the symbol table. You can think of this table as a structure that maps the name of the variable to the base structure.

  • It creates a reference to the base structure.

  • It increases the reference counter of the base structure in 1. This counter will keep track of how many variables point to the same base structure

variables in python

Id function and is operator

The id function returns the memory address of the reference structure object of your variable. You can use this function to compare two variables and see if they are pointing to the same object. The is operator is used to compare two variables and returns True if they are pointing to the same object.

Let´s see them in action to better understand how Python manages variable internally:

[ ]:
my_first_variable = 23
my_second_variable = my_first_variable

print("The value of my first variable is:")
print(my_first_variable)
print("Its memory address is:")
print(id(my_first_variable))

print("An the value of my second variable is:")
print(my_second_variable)
print("Its memory address is:")
print(id(my_second_variable))

print("Do both variables share the same memory address?")
print(my_first_variable is my_second_variable)

We do not have the same behavior when we assign two variables the same value:

[ ]:
my_first_variable = 1000
my_second_variable = 1000

print("The value of my first variable is:")
print(my_first_variable)
print("Its memory address is:")
print(id(my_first_variable))

print("An the value of my second variable is:")
print(my_second_variable)
print("Its memory address is:")
print(id(my_second_variable))

print("Do both variables share the same memory address?")
print(my_first_variable is my_second_variable)

Built-in getrefcount function

The built-in function sys.getrefcount returns the number of references to the variable that is passed as argument to the function, for intance:

[ ]:
import sys
x = 1.0
print(sys.getrefcount(x))
y = x
print(sys.getrefcount(x))

z = object()
print(sys.getrefcount(z))

Note that the result of getrefcount is always higher than expected because the function creates internally another reference. Note that the reference count for x is 3. This is because, when we assign a value to a variable, Python creates another object for that value, and all variables that are assigned the value reference the same memory address. So the reference count for x is 1 plus 1 for the reference of this internal object plus 1 of the getrefcount internal reference. Note that we did not assign an initial value to z, but instead, we initialise it with the construction function object which creates a base object. For this variable, the reference count is 2 because Python does not create another object for the value, since it is not provided in the assignment statement.

Immutable types

In Python, most primitive types are immutable. This means that whenever we make changes to a variable, Python does not actually change the value stored in the memory address, it rather creates a new base structure at another memory address and assigns the new value:

[29]:
another_variable = 27
print("the memory address of another variable is:")
print(id(another_variable))
another_variable = another_variable + 1
print("the memory address of another variable now is:")
print(id(another_variable))
the memory address of another variable is:
140718223313376
the memory address of another variable now is:
140718223313408

Garbage Collector

A process called garbage collector keeps track of the references to memory addresses and makes sure that unused memory (memory addresses to which no active variable is pointing) can be used again by any other process. It is important to bear in mind that although this process is transparent for developers, the performance of our code is going to depend on the way we manage variable assignments!