Python Fundamentals#
In this chapter, we will introduce you to the very basics of computer programming with Python. We will first see some general notions that will make you understand how a Python program looks. Then we will go through how to write basic instructions for reading and writing inside a computer program. We will also explain the types of variables we can use.
Introduction#
Python is a high-level programming language, like others you may have heard about, including Fortran, C or C++. High-level languages differ from low-level languages in that the former can be easily understood by people, while the latter are those that can be understood by machines. So when writing in a high-level language we normally need to translate the actions in our code to machine-readable format.
If you have ever done any programming before using languages like Fortran, C or C++, you surely adopted a workflow where you first wrote code and then compiled it using another program called compiler, wich generated a binary, machine-readable file. It would look something like this
That will not be the case with Python. One of the key differences between Python and those other programming languages is that Python is an interpretive language. Hence you will not have to compile the code as you modify it. The Python interpreter performs those actions for you.
All of these comes with a caveat. Because Python is not compiled, your programs written in Python will not be as fast as they could be when written in Fortran, C or C++. Below you can see a comparison between speeds of various programming languages
Hint
Clearly, Python is not the fastest language in town. There are workarounds for this, like linking Python and C or Fortran code, or using Cython.
Writing your first Python program#
Let’s see how running a simple Python program works in practice. Using your IDE or terminal, open a text editor and write the following:
#!/usr/bin/env python
# Our first Python program
print ("Hello World!")
Now let’s examine what we have just written.
You will notice that the first couple of lines
that we have written start with a hash symbol (#
).
This means that whatever follows will not be read by the
Python interpreter. This is what we usually regard as
a comment. Then there is a print
statement
followed by a parentheses ()
with a piece of text
inside. And that is all there is to our first Python
program.
Now close the file, save it as hello_world.py
and run
the following command on your terminal
foo@bar:~$ python hello_world.py
Now check whether the program has done as you intended it to.
Names and cases#
In your programs you will usually be defining lots of different
variables. Variables are names for values and the naming of
variables follow some conventions. For example, they can
be formed by letters, numbers and only one symbol, the
underscore _
.
Something else you must remember is that Python is case sensitive, so when you try to use a variable without regard to the case you used to assign it a value, things will not go well.
a = 10
print (A)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[1], line 2
1 a = 10
----> 2 print (A)
NameError: name 'A' is not defined
As we see, Python returns an error (specifically,
a NameError
), as a variable named A
does not exist.
The only variable that we assigned a value to is a
, and
in Python a
and A
are different things.
Hint
Always try to read error messages from Python. They are highly informative and often direct you to the exact line where the problem is.
Also, in Python some possibilities are forbidden, like starting a variable with a number. If you write code including something like:
77david = 'myname'
and try to run it, you will get a SyntaxError
. The same thing
will happen if you try to use one of the many keywords in
Python. These include
and del from not while
as elif global or with
assert else if pass yield
break except import print class
in raise continue is return
def for lambda try
It is convenient to choose variable names that will be meaningful to you and any other potential user of your program (often your future self). For example, if you are calculating the area of a circle, it would make sense to write something like this:
area = pi*radius**2
Here we are using some stuff that you we have not covered yet.
Also, we are assuming that you have given values to both pi
and radius
.
But you surely understand why we are using the code snippet as
an example of using meaningful variable names. Using cryptic
variable names will make everyone miserable, so it is a good
investment to use names that seem sensible choices.
Additionally, in Python there are naming conventions that you should follow. These are described in PEP8. For example, variables are typically written in lowercase, and so are functions. On the other hand, classes, which we will learn about in the future, use the so-called CapWords style.
Input / Output#
In our first program hello_world.py
above, we have already
written to something we call the “standard output”, i.e. the
display, using the print
function. The print
function
displays values as text.
In Python there are many other ways to write your output and also to read input into a program. You can, for example, make your program write the results of a calculation to a file instead of to your screen, or prompt the user to write a set of input variables using the keyword for the program to read (this would be the “standard input”). Here we explore the basics of Input / Output operations.
Opening and closing files#
Suppose that there is a file in your working directory
called myfile.txt
from which you want to read its contents.
In order to do that, you will have to write
f = open("myfile.txt", "r")
In this statement, we are using a built-in function called open
.
The first argument of this function is the external filename (Python
will assume that the file does exist). The second argument indicates
the processing mode. There are different modes available:
r
: read only modew
: write only modea
: append moder+
: read and write modet
: text modeb
: binary mode
Depending on the type of data in the file and what you intend to use the file for, you will choose one of these ways of opening the file. As a good programming habbit, remember to close files once you no longer need to access them:
f.close()
Another way of opening files in Python is using file iterators.
You are using them when you open files using the
with
statement, which works as
with open("myfile.txt", "r") as f:
...
In this case you do not need to close the file yourself.
Reading and writing data into files#
Once you have opened a file, you will many times want to read its
contents. You can do that line by line for the myfile.txt
which
you have opened and named as f
in your program doing
f.readline()
You can bulk-read all the file using readlines()
instead.
Alternatively, if you want to write into your file, you will first have to open it for writing. Then, you will be able to write into the file using the following statement
f = open("test.txt", "w")
f.write("hello world!\n")
f.close()
where we are simply writing a text string. As we go along, we will greatly expand our understanding of Input/Output operations.
Exercise
Do you remember our first Python program called hello_world.py
?
In a IPython session or using a Jupyter-notebook, open that
file using both open
and an iterator and write its contents
as text. Pay attention to how these are written.
Data Types#
Variables in Python can be of many different types, including text strings, lists, integers, floats and Boolean. In Python variables take the form of objects, although we will not explain what this highly consequential fact entails just yet. For now, we will just say that objects are pieces of memory, with values and sets of associated operations.
In Python, a variable is created when you assign a value to it.
Assignment of a value to a variable is simply made using the equal
operator (=
) in statements like
a = 10
Because Python has dynamic typing, you do not have to declare
variables. Python will assign a type to each variable in your code.
In order to know the type of a given variable, you can use
the intrinsic function type
. For the variable you just created
you can find the type using
type(a)
int
In the case above, you might want a
to be defined
as a float instead of an integer. If that is the case, you can assign
the type explicitly
a = float(10)
Alternatively, you can add a point at the end
a = 10.
The result will be the same.
In Python, you can sometimes combine different types to perform arithmetic operations
a = 10; b = 1.
c = a + b
print (c)
print (type(c))
11.0
<class 'float'>
Note
In the past, there were problems with integer divisions,
as you would always recover an int
, but that is no longer
the case in Python 3, where they are converted into a float.
Try the following:
a = 10; b = 3
c = a/b
print (c)
print (type(c))
Numeric types#
In the few lines of code above, we have used the most straightforward
types in Python, integers, which lack a decimal part,
and floating-point numbers.
Using floats and integers you can do all sorts of computations in Python,
very much like you would in a regular calculator. You can for example
use operators like +
and -
, which are called unary operators,
because they act on a single numeric value. You can perform multiplications,
using the operator *
, divisions with /
, and integer divisions with //
.
Using %
you will obtain the remainder of a division. Also you can
exponentiate using the operator **
.
There are also some additional operations that you
may want to know of, like the +=
addition assignment
or -=
substraction assignment. You can also use
relational operators, like <
, >
, <=
, >=
or ==
,
to compare the values of two variables. For example,
a = 10; b = 3
a < b
False
These operators will return either True
or False
.
This is what we call a Boolean.
We will discuss Booleans and logical operations in the
next chapter.
Something you must bear in mind when you are programming is the finite machine precision with which your computer works. A case in point is the following:
print (0.1 + 0.05)
0.15000000000000002
Discouragingly, the result is not what one would expect. There is, however, nothing wrong with that result. It is the consequence of the way real numbers are stored in your machine, which results in round-off or truncation errors. Specifically, floats are written using 53 bits of precision. Because 0.1 and 0.5 are truncated in floating-point representation, the result is not exactly what you would expect. When writing a program, you should think about whether this actually matters for the problem at hand.
Another type of numeric that you may need are complex numbers.
In Python, you can define them using the character j
.
type(1 + 1j)
complex
Exercise
Create two integer or floating point variables, a
and b
, and
assign them values. Perform the following operations and print the
results:
Addition of
a
andb
.Subtraction of
b from
a`.Multiplication of
a
andb
.Integer division of
a
byb
.Modulus (remainder) of
a
divided byb
.Exponentiation of
a
raised to the power ofb
. Check at every stage the types of the results.
Text strings#
The variable type we normally use to store text characters
in Python are strings. In fact, we have already written
one such variable in our hello_world.py
program above.
Strings are generated using single '
or double quotes "
around a set of characters. It is usually good to pick
a rule and stick to either single or double
quotes.
Python provides a number of intrinsic functions and operators
that are specific to string manipulation. For example, if
you want to know the length of a string, you can use the
len
function.
myname = 'david'
print (len(myname))
5
Additionally, addition and multiplication work in a special way when applied to strings. For example,
string1 = "hello"
string2 = "world"
space = " "
print (string1 + space + string2)
hello world
Also,
verse1 = "Good morning\n"
verse2 = "Nothing to do to save his life call his wife in"
print (5*verse1 + verse2)
Good morning
Good morning
Good morning
Good morning
Good morning
Nothing to do to save his life call his wife in
Hence, addition for strings means concatenation, while multiplication means repetition.
You can also perform membership operators, to check whether an element is present in a string.
'a' in 'Antonio'
False
Another interesting thing you can do with strings that
was not possible with int
or float
is indexing. You
can access the i-th element of a string using
string[i]
where you must remember that Python uses zero-based indexing. Hence, the 1st element in a string is accessed using the index 0, and so on. You can also access characters in a string backwards, using negative numbers. We can also perform slicing operations, which extract sections (or slices) of the string
val = 'spam'
print (val[2:4])
am
There is an interesting property of strings, which is called immutability. While you can redefine a string, you cannot change it. For example, you can do
s = 'spam'
s = 'ham'
but if you try
s = 'spam'
s[0] = 'h'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[12], line 2
1 s = 'spam'
----> 2 s[0] = 'h'
TypeError: 'str' object does not support item assignment
you will obtain a TypeError
. What you can do instead
is
s = 'h' + s[2:]
print (s)
ham
This construction generates a new string using the old string, but importantly, it does not change the old string.
There are many other interesting manipulations that we can do with text strings, using type-specific methods. Strings are, for example, searchable
s = 'david'
s.find('d')
0
and you can generate new strings making intuitive changes like making uppercase what was lowercase
s = 'peter'
s.upper()
'PETER'
You can also split a string into parts, based on a given element.
s = 'bread, butter, flour, milk'
s.split(',')
['bread', ' butter', ' flour', ' milk']
But this takes us directly into the next type of variable we want to consider.
Exercise
Create a string variable
text
and assign it a text of your choice. Use string methods to calculate the length of the string, convert the string to uppercase or lowercase and capitalize the first letter.Using string slicing, extract a substring from the middle of the
text
variable.Create two string variables,
str1
andstr2
, and assign them different text values. Concatenatestr1
andstr2
to create a new string.Use the
split()
method to split the text variable into a list of words.
F-Strings: Formatted String Literals#
In addition to basic string operations, Python offers a powerful feature called f-strings (formatted string literals) that allows for more dynamic and readable string formatting. F-strings were introduced in Python 3.6 and provide an intuitive way to embed expressions directly within string literals.
To create an f-string, simply prefix your string with the letter f
or F
. You can include variables or expressions inside curly braces {}
within the string, which will be evaluated at runtime.
For instance, consider the following code:
event_name = "Python Workshop"
date = "October 15, 2024"
location = "Community Center"
event_details = f"The {event_name} will be held on {date} at the {location}."
print(event_details)
the output is
The Python Workshop will be held on October 15, 2024 at the Community Center.
In this example, the variables event_name
, date
, and location
are directly embedded in the string, providing clear
information about the event.
F-strings also allow you to include expressions. For example:
width = 10
height = 5
area = f"The area of the rectangle is {width * height} square units."
print(area)
gives the output
The area of the rectangle is 50 square units.
Here, the expression width * height
is evaluated, and its result is included in the f-string.
Moreover, f-strings support formatting options, making it easy to control how numbers are displayed:
pi = 3.14159
formatted_pi = f"Pi rounded to two decimal places is {pi:.2f}."
print(formatted_pi)
and the output is
Pi rounded to two decimal places is 3.14.
In this example, the .2f
format specifier rounds pi to two decimal places.
We will see more on F-strings in when reviewing printing methods more in detail.
Lists, tuples and dictionaries#
Lists are a very important type of sequence, which in Python is a type of data structures that contain a collection of elements. In the last section, we have generated one such object
['bread', ' butter', ' flour', ' milk']
which is of course a list of strings.
Lists are defined when the list of elements is written inside square
brackets ([ ]
). If you were using parenthesis instead,
(( )
) then you would be defining what we call a tuple.
Finally, there are dictionaries, which are defined
using curly brackets ({ }
).
Lists and tuples can contain all sorts of data types. You can for example combine numerics and strings.
mylist = ["I", "am", 30, "years", "old"]
Also, you can easily convert between lists and tuples
mytuple = tuple(mylist)
The main difference between lists and tuples is that the latter are immutable, while you are allowed to change lists.
Operators work with lists and tuples like they did with strings. Addition is concatenation and multiplication is repetition. For example,
['one', 'two'] + ['three', 'four']
['one', 'two', 'three', 'four']
or
(1, 2)*3
(1, 2, 1, 2, 1, 2)
Membership operators are also similar to what we saw for lists
1 in [10, 20, 30]
False
As are the slicing operations, which are again accessing elements of the list using zero-based indexing
decades = list(range(10, 110, 10))
print (decades[2:-2])
[30, 40, 50, 60, 70, 80]
Here we are using something called a range
object,
which we are using to build a list. The syntax is worth
discussing: using range(start, stop, step)
we enumerate
integers from the
first argument (start
), to the second argument (stop
),
in steps of size given by the third argument (step
).
You can interrogate lists using intrinsic functions.
For example, you can find how many elements there are
in a list using len()
names = ['pedro', 'jose', 'felipe', 'mariano']
print (len(names))
4
and you can sort its contents with sorted()
names = ['pedro', 'jose', 'felipe', 'mariano']
print (sorted(names))
print (names)
['felipe', 'jose', 'mariano', 'pedro']
['pedro', 'jose', 'felipe', 'mariano']
which, as you sure have noticed, sorts strings alphabetically.
To finalize we will say that as objects, lists have associated methods that are particularly useful. For example, you can easily add an element to a list
names = ['pedro', 'jose', 'felipe', 'mariano']
names.append('yolanda')
print (names)
['pedro', 'jose', 'felipe', 'mariano', 'yolanda']
remove elements from a list
names = ['pedro', 'jose', 'felipe', 'mariano']
names.remove('pedro')
print (names)
['jose', 'felipe', 'mariano']
reverse the order of the elements
names = ['pedro', 'jose', 'felipe', 'mariano']
names.reverse()
print (names)
['mariano', 'felipe', 'jose', 'pedro']
or sort them. Remember that before we have used the function
sorted()
. There is an equivalent method:
numbers = [30, 1, 100]
numbers.sort()
print (numbers)
[1, 30, 100]
Exercise
Create a list called fruits with three fruit names, and print the first and last elements of the fruits list. Add a new fruit to the list and remove a fruit. At every stage, print the length of the fruits list.
Dictionaries are a more sofisticated type of sequence, with elements having multiple entries called keys and their corresponing values. Their structure is hence
mydict = {key1: val1, key2: val2, key3: val3}
There is great flexibility in terms of what can be a key or a value
presidents = {'Adolfo': [1976, 1977, 1979], 'Leopoldo': [1981], \
'Felipe': [1982, 1986, 1989, 1993]}
You can access dictionaries in many different ways, for
example using the items()
, keys()
or values()
method
presidents = {'Adolfo': [1976, 1977, 1979], 'Leopoldo': [1981], \
'Felipe': [1982, 1986, 1989, 1993]}
print (presidents.items())
print (presidents.keys())
print (presidents.values())
dict_items([('Adolfo', [1976, 1977, 1979]), ('Leopoldo', [1981]), ('Felipe', [1982, 1986, 1989, 1993])])
dict_keys(['Adolfo', 'Leopoldo', 'Felipe'])
dict_values([[1976, 1977, 1979], [1981], [1982, 1986, 1989, 1993]])
In the next few lessons we will put all these data structures to good use.
Exercise
Create a dictionary called student with your name and age. Print the student dictionary. Update the age to your current age. Add your university as a new key. Remove the “age” key. Print the updated student dictionary.