11  Operators

In programming, operators are special symbols that represent computations like addition, multiplication, and comparison. Python provides a wide range of operators that can be used to perform various operations on variables and values.

In this chapter, we will cover the following types of operators:

11.1 Assignment Operator

The assignment operator (=) is used to assign a value to a variable. The variable on the left side of the assignment operator is assigned the value on the right side. For example, in the code below, the variable x is assigned the value 5:

x = 5

11.2 Expressions

An expression is a combination of values, variables, and operators that evaluates to a single value. For example, 2 + 3 is an expression that evaluates to 5. Expressions can be simple or complex, depending on the number of values and operators involved.

Using the assignment operator, you can store the result of an expression in a variable. For example, in Listing 11.1, the result of the expression 2 + 3 is stored in the variable result.

Listing 11.1: Example of an assignment operator in Python. The expression 2 + 3 is assigned to the variable result. First, the expression 2 + 3 is evaluated, resulting in 5. Then, the value 5 is assigned to the variable result.
result = 2 + 3

You can think of result as a container in your computer’s memory that holds the value 5. From now on, you can use the variable result to access the value 5.

11.3 Arithmetic Operators

Arithmetic operators are used to perform mathematical operations like addition, subtraction, multiplication, and division. Below is a list of arithmetic operators available in Python:

Table 11.1: Arithmetic Operators.
Operator Symbol Operation
+ Addition
- Subtraction
* Multiplication
/ Division
** Exponentiation (Power)
// Floor Division (Integer Division)
% Modulo (Returns the remainder of a division)

11.3.1 Precedence

Precedence refers to the order in which operators are evaluated in an expression. In Python, operators have different levels of precedence, which determine the order in which they are evaluated. However, you can use parentheses to override the default precedence and control the order of evaluation. You can check the precedence of operators in the Python documentation.

For example, in Listing 11.2, you can see how the order of operations affects the result of an expression. Without parentheses, the order of operations is as follows:

  1. Exponentiation (**)
  2. Modulo (%)
  3. Multiplication (*)

Some operators, like % and *, have the same level of precedence. In this case, the operators are evaluated from left to right.

Listing 11.2: Example of arithmetic operators precedence.
result1 = 15 % 4 * 3 ** 2
# Step by step:
# 1. Exponentiation: 3 ** 2 = 9
# 2. Modulo: 15 % 4 = 3
# 3. Multiplication: 3 * 9 = 27
print("Result1 (without parentheses):", result1)

result2 = (15 % (4 * 3)) ** 2
# Step by step:
# 1. Parentheses: 4 * 3 = 12
# 2. Modulo: 15 % 12 = 3
# 3. Exponentiation: 3 ** 2 = 9
print("Result2 (with parentheses):", result2)
Result1 (without parentheses): 27
Result2 (with parentheses): 9
Use Parentheses to Control Precedence

To avoid confusion and ensure that your expressions are evaluated correctly, use parentheses to control the order of operations. This will make your code more readable and less error-prone.

In Listing 11.3, you can see an example of how parentheses can improve the readability of an expression involving a calculation of a grade, defined as: \[ \text{grade} = \text{min\_grade} + \left(\frac{\text{score}}{\text{max\_score}}\right) \times (\text{max\_grade} - \text{min\_grade}) \]

Listing 11.3: Example of using parentheses to control the order of operations in a grade calculation. With parentheses, you can add new lines and indentations to make the expression more readable.
score = 60
max_score = 100
min_grade = 1
max_grade = 10
grade = (
    min_grade + (
        (score / max_score)
        * (max_grade - min_grade)
    )
)

11.3.2 Syntax Errors

Python has a set of rules that define the correct structure of expressions. If you violate these rules, Python will raise a SyntaxError. For example, if you forget to close a parenthesis or use an invalid operator, Python will raise an error.

Syntax Errors in Natural Languages

In natural languages, syntax errors can occur when you use the wrong word order or violate grammar rules. For example, in Dutch, “het” and “de” are articles that precede nouns (translated simply as “the” in English). The choice between “het” and “de” depends on many factors. If you use the wrong article, the sentence will be incorrect. The compiler (or the Dutch listener) will raise an error (or figure out that you are not a native speaker) if you use the wrong article.

In Listing 11.4, you can see an example of a SyntaxError caused by an invalid expression. The error message indicates that the syntax is incorrect due to an invalid operator (* without a second operand).

Listing 11.4: A Python syntax error can occur due to various reasons, such as missing parentheses, invalid operators, or incorrect indentation. Understanding these errors is essential for debugging your code effectively.
(a) Example of a syntax error in Python. The code is missing the second operand for the multiplication operator.
# SyntaxError: invalid syntax
result = 2 + 3 *
(b) Output of the syntax error in Python. The caret (^) indicates the location of the syntax error.
  File "<python-input-0>", line 1
    result = 2 + 3 *
                    ^
SyntaxError: invalid syntax

11.3.3 Modulo and Floor Division

Besides the typical arithmetic operators, Python also provides some additional operators like:

  • Floor division (//): Returns the integer part of the division operation.
  • Modulo (%): Returns the remainder of the division operation.
Applications of Modulo Operator

Why are these operators useful? One common use case is to determine if a number is divisible by another number. For example, if you want to determine if a number n is prime (i.e., only divisible by 1 and itself), you can check if n is divisible by any number between 2 and n-1. If n is divisible by any number in this range, it is not prime.

Prime numbers have a range of applications in practice. In cryptography, for example, prime numbers are used to generate secure encryption keys (e.g., RSA algorithm).

In the tables below, you can see how the % and // operators work in Python for different inputs.

Table 11.2: Using the % operator, you can calculate the remainder of a division operation. Using the // operator, you can calculate the integer part of the division operation.
Using the % and // operators with n % 3 and n // 3.
Input (n) n // 3 n % 3
0 0 0
1 0 1
2 0 2
3 1 0
4 1 1
5 1 2
6 2 0
7 2 1
8 2 2
9 3 0
10 3 1
11 3 2
Using the % and // operators with n % 4 and n // 4.
Input (n) n // 4 n % 4
0 0 0
1 0 1
2 0 2
3 0 3
4 1 0
5 1 1
6 1 2
7 1 3
8 2 0
9 2 1
10 2 2
11 2 3

11.3.4 Other Arithmetic Functions

In Python, some arithmetic functions are available to perform specific operations. For example:

  • pow(x, y): Returns x raised to the power y.
  • abs(x): Returns the absolute value of x.
  • round(x): Rounds x to the nearest integer.

The math module provides functions for more complex mathematical operations. For example:

  • math.sqrt(x): Returns the square root of x.
  • math.floor(x): Returns the largest integer less than or equal to x (the “floor” of x).
  • math.ceil(x): Returns the smallest integer greater than or equal to x (the “ceiling” of x).

Also, the math module provides constants like math.pi and math.e for mathematical calculations.

11.4 Logical and Comparison Operators

Logical and comparison operators are used to create conditional expressions that evaluate to either True or False.

  • A comparison operator compares two values and returns True if the comparison is true, and False otherwise. For example, 5 > 3 is True because 5 is greater than 3.
  • A logical operator combines multiple conditional expressions and returns True or False based on the logical operation. For example, 5 > 3 and 7 < 10 is True because both conditions are true.

In Table 29.4, you can see a list of logical and comparison operators available in Python.

Table 11.3: Most common logical and comparison operators in Python.
Operator Symbol Operation
== Equal
!= Not Equal
> Greater Than
< Less Than
>= Greater Than or Equal
<= Less Than or Equal
and Logical AND
or Logical OR
not Logical NOT

11.4.1 Truth Tables

A truth table is a table that shows the possible values of logical expressions and their results. In Table 11.4, you can see the truth table for the logical operators and, or, and not.

Table 11.4: Truth table for the logical operators and, or, and not. The operands A and B can be either True or False.
A B A and B A or B not A
False False False False True
False True False True True
True False False True False
True True True True False

11.4.2 Precedence

All comparison operators have equal precedence, and all have greater precedence than the logical operators, but lower precedence than the arithmetic operators.

For logical operators, the precedence order is:

  1. Logical NOT (not)
  2. Logical AND (and)
  3. Logical OR (or)

For example, in Listing 11.5, you can see how the order of operations affects the result of an expression.

Result 1 is calculated following the steps:

  1. ((not (x < y)) and (y < z)) or (x < z)
  2. ((not (True)) and (True)) or (True)
  3. (False and True) or True
  4. False or True
  5. True

Result 2 is calculated following the steps:

  1. not (((x < y) and (y < z)) or (x < z))
  2. not ((True and True) or True)
  3. not (True or True)
  4. not (True)
  5. False
Listing 11.5: Examples of logical operators precedence.
x = 5
y = 10
z = 15

result1 = not x < y and y < z or x < z
print("Result 1:", result1)

result2 = not (x < y and y < z or x < z)
print("Result 2:", result2)
Result 1: True
Result 2: False

11.5 Combining Operators

Python provides shorthand operators that combine arithmetic operations with assignment. These operators are useful for updating the value of a variable based on its current value.

For example, in Listing 11.6, you can see how the shorthand operators work. The expression x += 5 is equivalent to x = x + 5.

Listing 11.6: Examples of shorthand operators in Python. The shorthand operators combine arithmetic operations with assignment.
x = 10
print("x =", x, end=" / ")
x += 5
print("x += 5:", x)

y = 20
print("y =", y, end=" / ")
y -= 10
print("y -= 10:", y)

z = 30
print("z =", z, end=" / ")
z *= 2
print("z *= 2:", z)

w = 40
print("w =", w, end=" / ")
w /= 4
print("w /= 4:", w)

v = 50
print("v =", v, end=" / ")
v %= 3
print("v %= 3:", v)
x = 10 / x += 5: 15
y = 20 / y -= 10: 10
z = 30 / z *= 2: 60
w = 40 / w /= 4: 10.0
v = 50 / v %= 3: 2

11.6 Exercises

11.6.1 Creating Functions for Arithmetic Operators

For each operator in Table 11.1, create a function that takes two numbers and returns the resulting value. Make sure your function is tested in a separate test function.

See below examples for the first three operators:

  • add,
  • multiply, and
  • divide.
def add(num1, num2):
    return num1 + num2
    pass

def multiply(num1, num2):
    return num1 * num2
    pass

def divide(num1, num2):
    return num1 / num2
    pass

def test_add():
    print("Result (1 + 2) =", add(1, 2))
    assert add(1, 2) == 3

def test_multiply():
    print("Result (1 * 2) =", multiply(1, 2))
    assert multiply(1, 2) == 2

def test_divide():
    print("Result (3 / 2) =", divide(3, 2))
    assert divide(3, 2) == 1.5

11.6.2 Precedence of Arithmetic Operators

Use your arithmetic functions to calculate the following expressions:

  1. 2 + 3 * 4
  2. (2 + 3) % 4
  3. 2 ** 3 + 4
  4. 2 ** (3 + 4)
  5. 2 * 30 // 4 % 2 + 5

For example, the first expression can be calculated as follows:

result1 = add(2, multiply(3, 4))
print("Result 1:", result1)
assert result1 == 2 + 3 * 4

11.6.3 Testing Functions

Add the programming logic to the functions defined below. Replace the pass statement with the code that implements the function’s logic.

def calculate_average_two_numbers(num1, num2):`
    '''Calculate the average of two numbers.

    Args:
        num1 (float): The first number.
        num2 (float): The second number.
    
    Returns:
        float: The average of the two numbers.
    '''
    pass # Add code here!

def calculate_hypotenuse(a, b):
    '''Calculate the hypotenuse of a right-angled
    triangle given the lengths of the other 
    two sides.

    Args:
        a (float): The length of side a.
        b (float): The length of side b.
    
    Returns:
        float: The length of the hypotenuse.

    Examples:
        >>> calculate_hypotenuse(3, 4)
        5.0
    '''
    pass # Add code here!

def convert_pounds_to_kgs(pounds):
    '''Convert pounds to kilograms.
    1 pound is equal to 0.453592 kilograms.

    Args:
        pounds (float): The weight in pounds.

    Returns:
        float: The weight in kilograms.

    Examples:
        >>> convert_pounds_to_kgs(2.2)
        1.0
    '''
    pass # Add code here!

For each function, write useful tests to check if the function is working as expected.

For example, the template code:

def calculate_circle_area(radius):
    '''Calculate the area of a circle given the radius.

    Args:
        radius (float): The radius of the circle.
    
    Returns:
        float: The area of the circle.

    Examples:
        >>> calculate_circle_area(10)
        314.16
    '''
    pass # Add code here!

Should become:

import math
def calculate_circle_area(radius):
    '''Calculate the area of a circle given the radius.

    Args:
        radius (float): The radius of the circle.
    
    Returns:
        float: The area of the circle.

    Examples:
        >>> calculate_circle_area(10)
        314.16
    '''
    return math.pi * radius ** 2

def test_calculate_circle_area():
    assert round(calculate_circle_area(5), 2) == 78.54
    assert round(calculate_circle_area(10), 2) == 314.16

# Call the test function to check if the function
# is working correctly.
test_calculate_circle_area()

Make sure your functions are tested across a range of inputs to ensure they work correctly.

Testing with pytest

You can use the pytest library to run your tests. To install pytest, you can use the following command:

pip install pytest

Then, you can run your tests by creating a file named test_arithmetic.py and adding your test functions to it. You can run the tests using the following command:

pytest test_arithmetic.py

11.6.4 Check if a number is even

Write a function that takes an integer as input and returns True if it’s an even number, otherwise False.

def is_even(num):
    pass # Add code here!

def test_is_even():
    assert is_even(4) == True
    assert is_even(7) == False
    assert is_even(0) == True

11.6.5 Check if a number is divisible by both 3 and 5

Write a function that takes an integer as input and returns True if it’s divisible by both 3 and 5, otherwise False.

def is_divisible_by_3_and_5(num):
    pass # Add code here!

def test_is_divisible_by_3_and_5():
    assert is_divisible_by_3_and_5(15) == True
    assert is_divisible_by_3_and_5(9) == False
    assert is_divisible_by_3_and_5(10) == False

11.6.6 Check if a string contains a specific character

Write a function that takes a string and a character as input and returns True if the string contains the character, otherwise False.

def contains_character(input_string, character):
    pass # Add code here!

def test_contains_character():
    assert contains_character("Hello, World!", "o") == True
    assert contains_character("Python Programming", "z") == False
    assert contains_character("12345", "5") == True

11.6.7 Check if two numbers are both positive

Write a function that takes two numbers as input and returns True if both numbers are positive, otherwise False.

def are_both_positive(num1, num2):
    pass # Add code here!

def test_are_both_positive():
    assert are_both_positive(5, 7) == True
    assert are_both_positive(-1, 2) == False
    assert are_both_positive(0, 0) == False

11.6.8 Check if a number is within a specific range

Write a function that takes a number and a range (minimum and maximum) as input, and returns True if the number is within that range, otherwise False.

def is_within_range(num, min_range, max_range):
    pass # Add code here!

def test_is_within_range():
    assert is_within_range(5, 1, 10) == True
    assert is_within_range(15, 1, 10) == False
    assert is_within_range(0, -1, 1) == True

11.6.9 Check if a string contains either “apple” or “banana”

Write a function that takes a string as input and returns True if it contains either “apple” or “banana” (case insensitive), otherwise False.

def contains_fruit(input_string):
    pass # Add code here!

def test_contains_fruit():
    assert contains_fruit("I love apples") == True
    assert contains_fruit("Banana is my favorite fruit") == True
    assert contains_fruit("Grapes are delicious") == False

11.6.10 Check if a number is not equal to 0

Write a function that takes a number as input and returns True if it’s not equal to 0, otherwise False.

def is_not_zero(num):
    pass # Add code here!

def test_is_not_zero():
    assert is_not_zero(5) == True
    assert is_not_zero(0) == False
    assert is_not_zero(-3) == True

11.6.11 Check if a string is not empty

Write a function that takes a string as input and returns True if it’s not empty, otherwise False.

def is_not_empty(input_string):
    pass # Add code here!

def test_is_not_empty():
    assert is_not_empty("Hello") == True
    assert is_not_empty("") == False
    assert is_not_empty("  ") == True

11.6.12 Check if two boolean values are both True

Write a function that takes two boolean values as input and returns True if both are True, otherwise False.

def are_both_true(bool1, bool2):
    pass # Add code here!

def test_are_both_true():
    assert are_both_true(True, True) == True
    assert are_both_true(True, False) == False
    assert are_both_true(False, False) == False

11.6.13 Check if a number is greater than 10 and less than 20

Write a function that takes a number as input and returns True if it’s greater than 10 and less than 20, otherwise False.

def is_between_10_and_20(num):
    pass # Add code here!

def test_is_between_10_and_20():
    assert is_between_10_and_20(15) == True
    assert is_between_10_and_20(5) == False
    assert is_between_10_and_20(20) == False

11.6.14 Check if two numbers have the same fractional part

Write a function that takes two numbers as input and returns True if they have the same fractional part, otherwise False.

def have_same_fractional_part(num1, num2):
    pass # Add code here!

def test_have_same_fractional_part():
    assert have_same_fractional_part(3.25, 7.25) == True
    assert have_same_fractional_part(2.5, 2.75) == False
    assert have_same_fractional_part(1.0, 2.0) == True
    assert have_same_fractional_part(1, 2) == True
    assert have_same_fractional_part(0, 0) == True

11.6.15 Check if a number is a multiple of 4 and 6

Write a function that takes an integer as input and returns True if it’s a multiple of both 4 and 6, otherwise False.

def is_multiple_of_4_and_6(num):
    pass # Add code here!

def test_is_multiple_of_4_and_6():
    assert is_multiple_of_4_and_6(12) == True
    assert is_multiple_of_4_and_6(24) == True
    assert is_multiple_of_4_and_6(8) == False

11.6.16 Check if two strings are equal (case insensitive)

Write a function that takes two strings as input and returns True if they are equal (case insensitive), otherwise False.

def are_strings_equal(str1, str2):
    pass # Add code here!

def test_are_strings_equal():
    assert are_strings_equal("Hello", "HELLO") == True
    assert are_strings_equal("Python", "Cobra") == False
    assert are_strings_equal("ArArA", "ArArA") == True

11.6.17 Check if String Contains a Character and Meets Conditions

Write a function called is_valid_string that takes three parameters: a string, a boolean, and a number. The function should return True if the following conditions are met:

  1. The string contains the character 'A' (case insensitive).
  2. The boolean parameter is True.
  3. The number parameter is greater than or equal to 10.

Otherwise, the function should return False.

def is_valid_string(input_str, flag, num):
    pass # Add code here!

def test_is_valid_string():
    assert is_valid_string("Sample String", True, 15) == True
    assert is_valid_string("Another String", False, 5) == False
    assert is_valid_string("A String with A", True, 8) == False
    assert is_valid_string("ABC", True, 10) == True

11.6.18 Create a function that runs all tests

def run_all_tests():
    pass # Add code here to call all test functions