# Addition
1 + 2 + 3 + 4 + 515
In this chapter, we introduce the fundamental building blocks of Python: objects and functions. In Python, almost everything is an object—including data, datasets, and even functions themselves. Functions perform actions on other objects, making them essential tools for working with data. We begin by exploring the concept of an object, along with the different data types and data structures that Python uses. Furthermore, we explain functions in more detail, discuss some of the main built-in functions in Python, and see how we can create our own functions.
Let’s start with a very simple arithmetic example. In the code below we employ addition, subtraction, multiplication, division, exponentiation and modulo (remainder of a division):
# Addition
1 + 2 + 3 + 4 + 515
# Subtraction
10 - 55
# Multiplication
2 * 36
# Division
5 / 41.25
# Exponentiation
2 ** 416
# Remainder
5 % 41
As explained in Chapter Introduction to Python and Visual Studio Code, we can type our code in the Code Editor and click Run. In the example above, the results appear below each line of code and we can therefore check them directly.
Code Comments
Like in other programming languages, we can make comments inside the code using hashtags (
#), as we did above, in order to be able to remember our actions or explain it to other analysts. When we use a hashtag in a line of code, Python does not try to process whatever we type after it—Python simply prints whatever we have typed.
One of the most important things we want to do in programming is to store the results of our functions so as to use them in subsequent parts of our programs. In Python, we use the equal sign (=) to assign a value or a result to a variable. A variable then stores a reference to our creation, which is called an object. An object is a data structure that holds values (or data), such as numbers, text, or more complex structures. These objects can be manipulated and analyzed using various functions and operations. In Python, nearly everything is an object. When we create a variable or data structure (such as a list or a function), we are actually creating an object.
Definition
In Python, an object refers to any data value (such as a number, string, list, or dictionary) that stores information, while a variable is a name or identifier that is used to reference an object in memory. Essentially, the object is the data, and the variable is the label for that data.
Python is an object-oriented language, meaning that all data values and functions are treated as objects with associated behavior and properties. For now, it is enough to think of objects as “things” stored in Python that we can work with and manipulate. This may seem a bit abstract now, but it will become clearer as we look at more examples. For now, we just need to remember that an object is something we have saved and can work with in Python.
To see how we can create and manipulate an object in Python, suppose we want to store the value 2 in the variable x. We can do this by using the following code:
# Assigning a numeric value to variable x
x = 2Generally, when we create a new object, we can give it any name we want. However, there are two things we need to keep in mind:
Easy name: It is better to choose a name that is related to the data so that everyone can easily understand what this object refers to. For example, an object that includes sales data could be named sales or sales_data.
Name Restrictions: There are self-explanatory restrictions when we create an object. For instance, a variable name cannot be a number, or the name of an existing function or built-in type, such as list().
Notice that when we make an assignment, Python does not print anything back. This behavior (non-printing) suggests that the object is defined successfully, otherwise we would receive an error. To see the results, it is necessary to “call” the object. We can do so using the function print() or simply by calling the object itself in an interactive environment:
# Printing variable x
print(x)2
# Printing variable x in an interactive environment
x2
Every time we print variable x, the value 2 is displayed. But what if we want to change the value of that variable to 3? To do that, we simply assign the new value to the same variable:
# Assigning a numeric value to variable x
x = 3
# Printing variable x
x3
Variable x has the value 3 now instead of 2. We substituted the previous value with a new one. If we wanted to also keep the value 2 to work with it, we should need to assign the value 3 to a different variable. Of course, this means we need to know when there is a need to create a new variable and when to update an existing one. We can use the function dir() to check all objects currently stored in our workplace.
# Assigning a numeric value to variable x
x = 2
# Assigning a numeric value to variable y
y = 3
# Printing variable x
x2
# Printing variable y
y3
# Printing the names of all existing variables
dir()['In',
'Out',
'_',
'_10',
'_11',
'_12',
'_13',
'_2',
'_3',
'_4',
'_5',
'_6',
'_7',
'__',
'___',
'__builtin__',
'__builtins__',
'__name__',
'__spec__',
'_dh',
'_i',
'_i1',
'_i10',
'_i11',
'_i12',
'_i13',
'_i14',
'_i2',
'_i3',
'_i4',
'_i5',
'_i6',
'_i7',
'_i8',
'_i9',
'_ih',
'_ii',
'_iii',
'_oh',
'exit',
'get_ipython',
'np',
'ojs_define',
'open',
'pd',
'plt',
'quit',
'sns',
'x',
'y']
Sometimes, we want to remove objects, for example to free up memory or because we do not need a particular object in our analysis. We can remove an existing object using the del statement. Let’s remove the object x and check the results using dir():
# Removing object
del x
# Printing the names of all existing objects
dir()['In',
'Out',
'_',
'_10',
'_11',
'_12',
'_13',
'_14',
'_2',
'_3',
'_4',
'_5',
'_6',
'_7',
'__',
'___',
'__builtin__',
'__builtins__',
'__name__',
'__spec__',
'_dh',
'_i',
'_i1',
'_i10',
'_i11',
'_i12',
'_i13',
'_i14',
'_i15',
'_i2',
'_i3',
'_i4',
'_i5',
'_i6',
'_i7',
'_i8',
'_i9',
'_ih',
'_ii',
'_iii',
'_oh',
'exit',
'get_ipython',
'np',
'ojs_define',
'open',
'pd',
'plt',
'quit',
'sns',
'y']
Indeed, the object x was removed successfully (it does not appear in the list).
In Python, data types are used to define the kind of value stored in a variable. They are important because they determine what operations can be performed on the data and how Python interprets it. Python has several built-in data types, which are automatically assigned when we create objects.
Numeric: Numeric data types are used to store real numbers, which can be expressed in terms of integers or floats:
# Assigning numeric value
x = 9999
# Printing x
x9999
# Assigning numeric value
y = 3.14
# Printing y
y3.14
Character: Also known as strings, character data types are used to store text data. Text is represented as a sequence of characters enclosed in either single quotes ('') or double quotes (""):
# Assigning a string value
text = "Hello World"
# Printing text
text'Hello World'
Logical: Logical data types represent Boolean values, which can be either True or False. Logical values are often used in conditional statements and logical operations. Unlike strings, logical values do not require quotes when assigned or printed:
# Assigning logical values
true_value = True
false_value = False
# Printing true_value
true_valueTrue
# Printing false_value
false_valueFalse
Date and Datetime: These data types are used to store… dates and times (yes, you guessed it!), such as year and month. Dates and times are handled using the module datetime. This allows us to work with dates such as year, month, and day:
from datetime import date
# Assigning a date value
publication_date = date(2026, 1, 1)
# Printing publication_date
publication_datedatetime.date(2026, 1, 1)
These are the fundamental data types in Python, and we can perform various operations and manipulations on these data types to analyze and visualize data effectively. As it is obvious, it is very important to keep in mind the data type of an object. Python still tries to assign a data type for each object that we create, inferring this assignment from the values assigned to it. We can always check the data type, or class, of an object with the function type(). Let’s use this function in the objects we created:
# Type of x
type(x)int
# Type of y
type(y)float
# Type of text
type(text)str
# Type of true_value
type(true_value)bool
# Type of false_value
type(false_value)bool
# Type of publication_date
type(publication_date)datetime.date
It is easy to make calculations using numeric objects instead of their assigned values. In other words, we assign a numeric value to a variable and can then use that variable in our calculations. As such, there is no need to remember what value we assigned to our objects; Python “stores” those values for us.
# Assigning value to variable x
x = 5
# Assigning value to variable y
y = 4
# Addition
x + y9
# Subtraction
x - y1
# Multiplication
x * y20
# Division
x / y1.25
# Exponentiation
x ** y625
# Remainder
x % y1
Earlier, we mentioned that knowing the data types is important because we can handle our data more efficiently. Although we can use the type() function to check the exact data type of an object, we can also use functions such as isinstance() to check whether a data object is of a specific type. In this case, we specify the data type directly inside the function. For example, we can check whether an object is integer (int), float (float), a string (str), or a boolean (bool). As a result, the function returns a logical value (True or False):
# Is it integer?
isinstance(1, int)True
isinstance(1.1, int)False
isinstance("1", int)False
isinstance(True, int)True
# Is it float?
isinstance(1, float)False
isinstance(1.1, float)True
isinstance("1", float)False
isinstance(True, float)False
# Is it text?
isinstance(1, str)False
isinstance(1.1, str)False
isinstance("1", str)True
isinstance(True, str)False
# Is it boolean?
isinstance(1, bool)False
isinstance(1.1, bool)False
isinstance("1", bool)False
isinstance(True, bool)True
Suppose we have an object that we want to convert to a different data type (assuming conversion is possible). For this, we use built-in conversion functions such as int(), float(), str(), and bool(). This is, generally, a very useful approach because it allows us to easily change the data type of an object instead of trying to do so manually:
# Conversions to numeric
int(2)2
int("2")2
int(True)1
# Conversions to float
float(2)2.0
float("2")2.0
float(True)1.0
# Conversions to character
str(2)'2'
str("YES")'YES'
str(True)'True'
# Conversions to logical
bool(0)False
bool(2)True
bool("YES")True
bool("")False
bool(True)True
There are some interesting observations in the example above. For one, almost every data type can be converted into a string. For example, Python can interpret the value 2 as the string '2', a value that is different from the numeric value 2. Additionally, in Python, the value 0 is interpreted as False, while any other numeric value is interpreted as True. Empty strings are also interpreted as False, while non-empty strings are interpreted as True. Note that if a conversion is not possible, Python raises an error.
All the transformations mentioned above are possible because Python uses dynamic typing, meaning that types are assigned automatically, but conversions must be explicitly defined. This helps avoid ambiguity, but it also means that incorrect conversions will usually result in errors rather than automatic interpretation.
Up to this point, we saw the different data types in Python. A related but more advanced concept is that of data structures. While data types describe a single value, data structures allow us to store and organize multiple values together in a meaningful way. This is especially important when working with real datasets, where we rarely deal with single values in isolation.
In Python, the main fundamental data structures are: lists, tuples, sets, and dictionaries. In data science contexts, another fundamental data structure is arrays/data frames (via external libraries like pandas), which we discuss in the next chapters.
Lists: A list is one of the most important and flexible data structures in Python. It is used to store an ordered collection of items, and these items do not need to be of the same type. Lists are created using square brackets []:
# Assigning values to a list
our_list = [100, -100, "TEXT", 102, -98]
# Printing our_list
our_list[100, -100, 'TEXT', 102, -98]
We can even have lists within a list. This is often called a nested list, meaning that each element of the main list can itself be another list:
# Creating a double list
our_double_list = [
[100, -100, 102, -98],
["TEXT 1", "TEXT 2", "TEXT 3"]
]To select elements from a list, we use square brackets [] together with the index position of the element. Python starts counting from 0 instead of 1, meaning that the first element has index 0, the second has index 1, and so on.
# Selecting the fist element from the list
our_list[0]100
# Selecting the third element from the list
our_list[2]'TEXT'
When we work with nested lists, we can use multiple square brackets to move through the different levels of the structure. In the following example, our_double_list[0] selects the first internal list, while the second [1] selects the second element from that list:
# Selecting an element from a nested list
our_double_list[0][1]-100
Python also allows us to select multiple elements at once through a technique called slicing. Slicing uses the form [start:end], where the starting index is included while the ending index is excluded:
# Selecting the first three elements
our_list[0:3][100, -100, 'TEXT']
In this example, Python selects the elements with indexes 0, 1, and 2, but not 3.
In a list, we can also substitute values by assigning a new value to a specific index position:
# Changing the last value
our_list[4] = 4
# Printing updated list
our_list[100, -100, 'TEXT', 102, 4]
Lists can also be extended by adding new elements. One simple way to do this is by using the + operator. In this case, the new element must also be provided in the form of a list, even if we only want to add a single value:
# Extending the list
our_list = our_list + [120]
# Printing updated list
our_list[100, -100, 'TEXT', 102, 4, 120]
An important concept in Python is that lists are mutable, meaning that they can be modified after creation. Because of this, assigning one list to another variable does not create a completely independent copy. Instead, both variables refer to the same underlying list in memory. To create an actual copy of a list, we can use the function list():
# Creating a copy of a list
our_list_copy = list(our_list)
# Changing all elements of the copied list
our_list_copy[0:5] = [0, 0, 0, 0, 0]
# Printing all elements of the copied list
our_list_copy[0, 0, 0, 0, 0, 120]
# Printing all elements of the initial list
our_list[100, -100, 'TEXT', 102, 4, 120]
Notice that in this example, we change all the values in our_list_copy, but the values in our_list remain unchanged. This demonstrates that our_list_copy is an independent copy of the original list rather than another reference to the same list in memory. This is useful because it allows us to modify the copied list without affecting the original one.
Tuples: A tuple is similar to a list, but it is immutable, meaning its values cannot be changed after creation. Tuples are created using parentheses ():
# Creating a tuple
our_tuple = (100, -100, 105, 102, -98)
# Printing our_tuple
our_tuple(100, -100, 105, 102, -98)
To select elements in a tuple, we can still use square brackets [] together with the index position of the element, exactly as we do with lists:
# Selecting the first element
our_tuple[0]100
# Selecting the first three elements
our_tuple[0:3](100, -100, 105)
As stated, we cannot directly add new elements to an existing tuple; this would still count as modification. However, we can create a new tuple by combining tuples together using the + operator:
# Creating a new tuple with additional values
our_tuple = our_tuple + (120,)# Printing updated tuple
our_tuple(100, -100, 105, 102, -98, 120)
Although this looks like we are “extending” the tuple, what actually happens is that Python creates a completely new tuple that contains all the elements of the original tuple plus the new values. The original tuple itself remains unchanged, and the variable is simply reassigned to this new object. Also, notice that when we create a tuple with a single element, we still need to include a comma after the value. Without the comma, Python does not interpret the expression as a tuple.
Sets: A set is an unordered collection of unique values. In Python, we use curly braces {} to create a set. Because sets can only contain unique elements, duplicate values are automatically removed:
# Creating a set
our_set = {100, -100, 105, 102, -98, 100}
# Printing our_set
our_set{-100, -98, 100, 102, 105}
We can see that repeated values appear only once. Sets are useful when we are interested in unique elements only.
Like lists, sets are mutable, meaning that we can add or remove elements after creation. However, because sets are unordered, we cannot rely on positions or indexing to access specific elements. To add new elements in a set, we use the method add() like this:
# Adding a new element
our_set.add(120)
# Printing updated set
our_set{-100, -98, 100, 102, 105, 120}
To add multiple elements at once, we use the method update():
# Adding multiple elements
our_set.update([130, 140])
# Printing updated set
our_set{-100, -98, 100, 102, 105, 120, 130, 140}
Similarly, we can remove elements using remove() (which raises an error if the element does not exist) or discard() (which does not):
# Removing an element
our_set.remove(100)
# Safely removing an element
our_set.discard(-999)
# Printing updated set
our_set{-100, -98, 102, 105, 120, 130, 140}
An important property of sets is that they automatically enforce uniqueness. This means that even if we try to add duplicate values, they will not be stored more than once. This makes sets particularly useful when we want to extract or maintain only distinct values from a dataset.
Dictionaries: A dictionary is a data structure that stores data in key–value pairs. Each key is linked to a value, similar to how a word is linked to its definition in a real dictionary. In Python, dictionaries are created using curly braces {}, where each key is followed by a colon : and its corresponding value:
# Creating a dictionary
our_dict = {
"x": 100,
"y": -100,
"z": 105
}
# Printing object our_dict
our_dict{'x': 100, 'y': -100, 'z': 105}
Unlike lists or tuples, we do not use numeric positions to access elements in a dictionary. Instead, we use the corresponding key to retrieve the value:
# Accessing values of our_dict
our_dict["x"]100
our_dict["y"]-100
We can also access the keys, values, or items (key–value pairs) of a dictionary using the methods keys(), values() and items() respectively:
# Accessing the keys of our_dict
our_dict.keys()dict_keys(['x', 'y', 'z'])
# Accessing the values of our_dict
our_dict.values()dict_values([100, -100, 105])
# Accessing the items of our_dict
our_dict.items()dict_items([('x', 100), ('y', -100), ('z', 105)])
Dictionaries are mutable, meaning we can modify them after creation. For example, we can change an existing value by assigning a new one to a key:
# Updating a value
our_dict["x"] = 999
# Printing updated dictionary
our_dict{'x': 999, 'y': -100, 'z': 105}
We can also add new key–value pairs in the following way:
# Adding a new key-value pair
our_dict["w"] = 200
# Printing updated dictionary
our_dict{'x': 999, 'y': -100, 'z': 105, 'w': 200}
Similarly, we can remove elements using the del statement:
# Removing a key-value pair
del our_dict["y"]
# Printing updated dictionary
our_dict{'x': 999, 'z': 105, 'w': 200}
Dictionaries are extremely useful for structured data and are widely used in data science and programming because they allow us to store and retrieve information efficiently using meaningful labels instead of numeric positions.
Just like we used the data type values on an object with the isinstance() function, we can use the data structure values in the same way:
# Checking the data structure of our_list
isinstance(our_list, list)True
isinstance(our_list, tuple)False
isinstance(our_list, set)False
isinstance(our_list, dict)False
# Checking the data structure of our_tuple
isinstance(our_tuple, list)False
isinstance(our_tuple, tuple)True
isinstance(our_tuple, set)False
isinstance(our_tuple, dict)False
# Checking the data structure of our_set
isinstance(our_set, list)False
isinstance(our_set, tuple)False
isinstance(our_set, set)True
isinstance(our_set, dict)False
# Checking the data structure of our_dict
isinstance(our_dict, list)False
isinstance(our_dict, tuple)False
isinstance(our_dict, set)False
isinstance(our_dict, dict)True
Lastly, we can take advantage of the unique properties of each data structure for specific tasks. For instance, in order to keep only the unique elements of a list, we can convert the list into a set:
# Converting list to a set to keep only unique values
set_with_list_values = set(our_list)
# Printing set_with_list_values
set_with_list_values{-100, 100, 102, 120, 4, 'TEXT'}
Notice that the value 100 appears only once in the resulting set, even though it occurred twice in the original list.
In Python, a function is an organized, reusable piece of code that performs a specific task. Functions are typically recognized by the presence of parentheses (), within which we provide the inputs that the function requires. These inputs are called arguments. Arguments are the values passed into a function when it is called, allowing the function to perform its operations.
We’ve already used some functions, such as print(), isinstance() and type(), without explicitly specifying argument names. While every function in Python has parameters, many of them have default values, which means we do not always need to specify all arguments explicitly. When a function does not require any inputs, it is simply called with empty parentheses.
For example, the isinstance() function can be used by directly passing the object and the type we want to check:
# Checking whether an object is a list
isinstance([1, 2, 3], list)True
In more complex functions, it is often good practice to use named arguments because this improves readability and reduces confusion when functions have many parameters.
Although Python and its built-in libraries provide many functions, we can also create our own. For instance, suppose we want to create a function that calculates the sum of all numbers in a sequence:
# Creating a function
def our_function(first_number, last_number):
numbers_range = range(first_number, last_number + 1)
return sum(numbers_range)
# Calculating the sum of integers from 1 to 10 using our_function
our_function(first_number = 1, last_number = 10)55
Let’s explain what we did exactly. As a first step, we defined a function using the def statement, and gave it two parameters: first_number and last_number. These names are of our choice (we have seen how other functions use arguments). Inside the function, we created a sequence of numbers using range(), and assigned these results to numbers_range (again, a name of our choice). In Python, range() generates numbers starting from the first value up to (but not including) the last value, which is why we use last_number + 1. Then, we used the function sum(), which adds up all the values in a sequence. Finally, we used return to output the result.
Global vs Local Scope
Objects created inside a function are said to belong to the local scope, meaning they only exist within that function. Objects created outside functions belong to the global scope, meaning they are accessible throughout the entire program. Understanding this distinction is important because variables created inside functions cannot be directly accessed outside them.
A quicker but less structured way to create custom functions in Python is by using lambda functions, which are small, anonymous functions that can be written in a single line. They are useful when we need a simple function for a short task and do not want to formally define it using def. Unlike regular functions, lambda functions do not use a return statement or a function name. Instead, they are written in a compact form using the keyword lambda. For example, the following lambda function adds 10 to a number:
# Lambda function that adds 10 to a number
add_ten = lambda x: x + 10
# Using the function
add_ten(5)15
In this case, x is the input (argument), and x + 10 is the operation that is returned automatically.
Now, let’s look at some important built-in Python functions. To understand how these functions operate on collections of values, we first create a suitable list:
# Creating the object list_for_functions
list_for_functions = [100, 1, 2, 3, 4, 5, 6, 6, 6]The function sorted() is used to return a sorted version of a collection of values. In contrast to methods that modify an object directly, sorted() does not change the original object. Instead, it creates a new sorted list and returns it. This function can be applied not only to lists, but also to other objects such as tuples. For example, if we want to sort the elements in our list in ascending order, we can do the following:
# Sorting the list
sorted(list_for_functions)[1, 2, 3, 4, 5, 6, 6, 6, 100]
In this case, Python returns a new list where all values are arranged from the smallest to the largest. Importantly, the original list remains unchanged unless we explicitly reassign the result to a new or existing variable.
The functions max() and min() can be used to find the maximum and minimum value of a vector, respectively:
# Finding the maximum value
max(list_for_functions)100
# Finding the minimum value
min(list_for_functions)1
Instead of the values themselves, we can use the method index() to find specific values such as the minimum and the maximum values. Python prints the first index of the maximum or minimum value in our list:
# Finding the index of the maximum value
list_for_functions.index(max(list_for_functions))0
# Finding the index of the minimum value
list_for_functions.index(max(list_for_functions))0
Notice how we referred to index() as a method instead of a function. Even though functions and methods are closely related and sometimes used in similar ways, they are not the same. A function is a standalone piece of code that performs an operation when we pass data to it as input. For example, max() and min() are functions because they take a list as an argument and return a result. A method, on the other hand, is a function that is attached to a specific object and is called using dot (.). This means that the operation is applied directly to the object itself. For example, index() is a method of Python lists because it is called on a list and operates on its contents. We also discussed earlier in this chapter other methods such as keys() and values() in Python dictionaries. In short, functions take inputs as arguments, while methods belong to objects and are called through those objects using dot (.) syntax.
Last but not least, it is worth mentioning two more functions: range() and len(). We have already seen the range() function; it creates a sequence of numbers based on a starting value, an ending value, and a step size. For instance, we can use this function to generate all even numbers from 0 to 40 (0, 2, 4, etc.). Note that we need to transform the output into a list because range() itself produces a range object, which is not displayed as a full list of values:
# Creating a list of even numbers from 0 to 40
even_numbers = list(range(0, 40, 2))
# Printing even_numbers
even_numbers[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38]
The len() function stands for length, and returns the number of elements in a list (or any other collection). For example, the number of all even numbers between 0 and 40 can be calculated in the following way:
# Printing the number of the elements of even_numbers
len(even_numbers)20
In Python, control flow refers to the mechanisms that determine the order in which code is executed. Instead of running line by line in a strictly linear way, control flow allows us to make decisions, repeat actions, and build more dynamic programs. The two most fundamental control flow tools are conditional statements (decisions) and loops (repetition).
A conditional expression in Python allows us to return different values depending on whether a condition is True or False. In simple terms, it works like an if statement written in a compact form: if a condition is satisfied, we return one value; otherwise, we return another value. For example, suppose we have an object z, which we want to check whether it has a value higher than 2.5:
# Creating object z
z = 3
# Checking whether z is greater than 2.5
print(z > 2.5)True
A more structured way to write conditional expressions has the following format:
# If-else format
if condition:
expression
elif condition:
expression
else:
expressionHere, we use the statements if, elif, and else to control the flow of the program based on whether conditions are met or not. The code is evaluated from top to bottom, and the first condition that is True is executed. If none of the conditions are satisfied, the else block is executed.
For example, suppose we want to check the value of z and classify it based on its size. If it is greater than 4, we print one message; if it is exactly 4, we print another message; otherwise, we print a third message:
# Conditional statements
if z > 4:
print("z is higher than 4.")
elif z == 4:
print("z is equal to 4.")
else:
print("z is lower than 4.")z is lower than 4.
As expected, since z = 3, the condition z > 4 is not satisfied, and the program moves to the else block, which is executed.
With loops, we can create a piece of code that repeats itself. For instance, although not very useful in practice, we can create a for-loop that prints every element of our list individually. Although we could use the print() function to print all the elements at the same time, let us—for the sake of the example—print each element individually:
# Printing all elements together
print(list_for_functions)[100, 1, 2, 3, 4, 5, 6, 6, 6]
# Printing each element separately with a for-loop
for i in range(0, 9):
print(list_for_functions[i])100
1
2
3
4
5
6
6
6
For-loops take an index (usually denoted with i but we can choose any other letter or name) that receives one value of a sequence at a time and holds this value for one round (loop). When the code has run in the loop, the index takes the next value in the sequence (which was created using the range() function) and the code runs again. This whole process happens until i has taken all the values of the sequence. In our example, the index i first takes the value 1 and the code inside the loop runs. The code simply prints the element i (which, in the first run, is 1) of our sequence Subsequently, i takes the value 2 and the code runs again until i gets all 9 values and the code has run 9 times. Note that, in this example, we fill the number 9 manually, meaning that the for-loop would run 9 times, even if the number of elements was different.
Apart from for-loops, we can also use while-loops, which repeat a block of code as long as a given condition remains True. Unlike for-loops, which run for a predetermined number of iterations, while-loops continue until a stopping condition is met:
# Inserting the value of -3
z = -3
# Creating a while loop
while z != 0:
print("z is still not equal to 0")
if z > 0:
z = z - 1
else:
z = z + 1
print(z)z is still not equal to 0
-2
z is still not equal to 0
-1
z is still not equal to 0
0
In the example above, we start with the value -3 in the object z and then enter a while-loop. Here, as long as z is not equal (!=) to 0, the loop continues running. Inside the loop, Python checks whether z is greater than 0. If this is the case, it decreases the value of z by 1 to move it closer to 0. If z is not greater than 0, then it must be below 0, and therefore its value is increased by 1 to also move it toward 0. This process continues until z eventually reaches 0, at which point the conditionz != 0 becomes False and the loop stops.
It is very important to be careful with while-loops, because if the condition is never updated in a way that makes it False, the loop will run indefinitely. This is known as an infinite loop, and it can cause the program to freeze or keep running without stopping.
Before concluding this chapter, it is very important to discuss what we can do when we need help with a function. It is natural that sometimes we are not sure about the arguments of a function or what kind of input is expected. There can also be times when we forget the exact name of a function or how it should be used.
Python provides built-in documentation for almost every function, which we can access in different ways. One of the most common methods is using the function help(), where we pass the name of the function we want to learn more about. For instance, suppose we want to check the documentation of the function log() from the math module, a function that computes the natural logarithm of a number:
import math
# Help with the help() function
help(math.log)Help on built-in function log in module math:
log(...)
log(x, [base=math.e])
Return the logarithm of x to the given base.
If the base is not specified, returns the natural logarithm (base e) of x.
The documentation is displayed directly in the output area of the environment. This is a very useful feature of Python, and it works without needing an internet connection. Since it is common to forget the exact name of a function or its arguments, having this built-in support is especially helpful when learning new functions.