0% found this document useful (0 votes)
8 views

1_PythonBasics

Uploaded by

Daniel Yebra
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views

1_PythonBasics

Uploaded by

Daniel Yebra
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 107

PROGRAMMING FOR MACRO-FINANCE

Unit 1: Basic Elements of Python

Daniel Arrieta
[email protected]

IE University

Academic year: 24-25


Outline

Programs, Commands, and Definitions

Objects, Expressions, and Operators

Conditional Statements and Loops

Functions, Scoping, and Abstraction

Strings, Tuples, and Ranges

Lists and Mutability

Sets and Dictionaries


Introduction
Once the basics of computer programming are known, this skill can be
applied to any programming language. In this course, we will use
Python for learning how to write programs that solve problems.
Python is a general-purpose programming language which can be used
effectively to build almost any kind of program that does not need
direct access to the computer’s hardware.
Python is a relatively simple language that is easy to learn. It is
designed to be interpreted, so it can provide the kind of runtime
feedback that is especially helpful to novice programmers.
A large and growing number of freely available libraries interface to
Python and provide useful extended functionality.
In this unit we will present the building blocks of the Python
programming language.
The main reference for the unit are the first chapters of “Introduction
to Computation and Programming Using Python” by Guttag (2021).
1/97
Unit contents
This unit gathers some of the basic elements of Python.
Programs: They are also called scripts, and they are files containing
a sequence of Python code instructions that can be executed by the
Python interpreter. Scripts are written in plain text files with the
“.py” extension.
Functions: Python allows to define functions to encapsulate reusable
blocks of code. Functions can take parameters and return values. In
addition, Python provides built-in functions, e.g., to read input from
the user and display output to the console, or for basic mathematical
operations.
Variables and data types: Python allows you to assign values to
variables, which can be of different data types such as integers, floats,
strings, booleans, etc.
Control flow: Python provides control flow statements like if-else,
for loops, and while loops.
2/97
Unit contents (ii)
Structured types: in Python refer to data structures that allow to
organize and store related data in a specific format.
Strings are the first structured type that has been presented. In the
following, four additional structured types will be introduced.
These types provide a way to represent complex data structures and
enable efficient manipulation and access to the data.
Common Python structured types include tuples, lists, sets, and
dictionaries.
Each of these types has its own characteristics and is used for
different purposes.
The tuple type is a simple generalization of the str type, and, as it
happened with the strings, the tuples are also inmutable.
In contrast, list and dict types are mutable, and the consequences
of this will also be detailed in this section of the unit.

3/97
Next

Programs, Commands, and Definitions

Objects, Expressions, and Operators

Conditional Statements and Loops

Functions, Scoping, and Abstraction

Strings, Tuples, and Ranges

Lists and Mutability

Sets and Dictionaries


Python programs and commands
A Python program, sometimes called a script, is a sequence of
definitions and commands. The Python interpreter evaluates the
definitions and executes the commands.
A command, often called a statement, instructs the interpreter to do
something.
For example, open your IDE and create a Python file, i.e. with “.py”
extension, named “U1_0_print_example” with the following
sequence of commands:

print ( ' Programming is very useful ')


print ( ' Python is fun ! ')
print ( ' Programming is very useful , ' , ' Python is fun ! ')

The print function takes a variable number of arguments separated by


commas and prints them, separated by a space character, in the order
in which they appear.
4/97
Executing the commands
Using VS Code for running the script, this is

produces the output

This is what is called “running the Python script in a Terminal”, that


is why it is displayed within the tab TERMINAL in the VS Code bottom
pane.
5/97
Python definitions
A Python definition refers to the process of creating a function in
Python using the “def” keyword.
Functions in Python are blocks of reusable code designed to perform a
specific task.
They can be defined using the following syntax:
def function_name ( parameters ) :
# code block

- “def” is used to indicate the start of a function definition.


- “function_name” is the name given to the function.
- “parameters” are optional input values that can be passed to
the function. They are enclosed in parentheses and separated by
commas.
- “code blocks” is the set of instructions that the function
executes when called. It is indented under the function definition.
6/97
Definition example
Let’s take an example to illustrate the concept of a Python function
definition:
def greet ( name ) :
print ( f " Hello , { name }! " )

# Call the function


greet ( " John " )

In this example, we define a function called “greet” that takes a


single parameter name.
The function simply prints a greeting message with the provided name.
The line of the above code snippet preceded by the character “#” is a
comment, this is, code that will not be executed by the interpreter.
Last line of the code simply calls the defined function with the string
"John" as input.

7/97
Definition example (ii)
To use this function, we can call it like this:

This yields the output in an interactive Window:

8/97
Python shell and VS Code interactive window
The Python shell, also known as the Python interpreter or REPL
(Read-Eval-Print Loop), is an interactive command-line interface
where Python code can be written and executed.
It provides an interactive environment that allows to write and run
code snippets, test ideas, explore data interactively, and get
immediate feedback.
The Python interactive window in VS Code offers features similar to a
Python REPL or console.
It allows to type Python code directly into the window and see the
results immediately without needing to save and run a separate script.
When a Python shell is launched, a prompt is presented, typically
“>>>”.

Once statements and expressions have been typed, when Enter key is
pressed, the Python interpreter evaluates the code and displays the
result, if any.
9/97
VS Code interactive window functionalities
Once interactive window in VS Code has been launched, at the
bottom is displayed

where Python statements and expressions can be entered.


Next are gathered some key features and functionalities of the Python
interactive window in VS Code.
Running code interactively: As aforementioned, Python code can
be entered directly in the interactive window and execute it by
pressing the Enter key. The results of the code execution will be
displayed in the output area below.
Variable and object inspection: User can inspect and explore the
values of variables, objects, and expressions in the interactive window.
This is useful for understanding the state of your program at different
points during execution.
10/97
VS Code interactive window functionalities (ii)
History and command recall: The interactive window keeps a
history of all the code snippets that have been executed. The up and
down arrow keys cab be used to navigate through the history and
recall previous commands.
Code completion and IntelliSense: VS Code provides code
completion and IntelliSense suggestions as the user types in the
interactive window. It helps to quickly write code by offering
suggestions for functions, methods, variables, and other objects based
on the context.
Plotting and visualization: The interactive window supports
plotting and visualization libraries such as Matplotlib. Graphs, charts,
and visualizations can be created directly in the window and interact
with them.
Debugging support: User can set breakpoints, step through code,
and debug Python programs directly in the interactive window. This
allows to interactively debug the code and investigate any issues.
11/97
Built-in Functions
Python has many built-in functions that are commonly used for
various purposes.
These functions are available for use without the need for any
additional imports or installations.
They cover a wide range of functionalities, including mathematical
operations, string manipulation, file handling, and more.
The main built-in functions are summarized next.
String Functions
len(): Returns the length of a string.
str(): Converts objects into a string representation.
upper(): Converts a string to uppercase.
lower(): Converts a string to lowercase.
split(): Splits a string into a list of substrings based on a
specified delimiter.
12/97
Built-in Functions (ii)
Mathematical Functions
abs(): Returns the absolute value of a number.
pow(x,y): Returns the value of x to the power of y.
round(): Rounds a number to the nearest integer or specified
decimal places.
File Handling Functions
open(): Opens a file and returns a file object.
read(): Reads the contents of a file.
write(): Writes the specified content to a file.
Other Important Functions
type(): Returns the type of an object.
input(): Reads a line from input and returns it as a string.
range(): Generates a sequence of numbers.

13/97
Next

Programs, Commands, and Definitions

Objects, Expressions, and Operators

Conditional Statements and Loops

Functions, Scoping, and Abstraction

Strings, Tuples, and Ranges

Lists and Mutability

Sets and Dictionaries


Code, data, objects, and types
The two basic building blocks of programming are code and data.
Code is a set of instructions that tell the computer what to perform
and how it should perform.
Data refers to the quantities, characters, and/or symbols on which
operations are performed with a computer
Objects are the core things that Python programs manipulate. Each
object has a type that defines what programs can do with that object.
Types are either scalar or non-scalar. Scalar objects are indivisible,
they can be think of as the atoms of the language.
Nonscalar objects, for example strings, have internal structure. Many
types of objects can be denoted by literals in the text of a program.
For example, the text 2 is a literal representing a number and the text
'abc' is a literal representing a string.
Python has several types of scalar objects that will be explained next.
14/97
Scalar objects
Python has four types of scalar objects:
i. int is used to represent integers. Literals of type int are written
in the usual way to denote integers (e.g., -3 or 5 or 10002).
ii. float is used to represent real numbers. Literals of type float
always include a decimal point (e.g., 3.0 or 3.17 or -28.72).
iii. bool is used to represent the boolean values True or False.
iv. None is a type with a single value.
The built-in Python function type can be used to find out the type of
an object.
Within the computer, values of type float are stored as
floating-point numbers.
This representation is used by all modern programming languages.
However, under some situations, it causes floating-point arithmetic to
behave slightly different from arithmetic on real numbers.
15/97
Expressions and operators
Objects and operators can be combined to form expressions, each of
which evaluates to an object of some type. This is called the value of
the expression.
For example, the expression 3 + 2 denotes the object 5 of type int,
and the expression 3.0 + 2.0 denotes the object 5.0 of type float.
The “==” operator is used to test whether two expressions evaluate to
the same value.
The “!=” operator is used to test whether two expressions evaluate to
different values.
A single “=” means something quite different, as we will see.
Be forewarned, you will make the mistake of typing “=” when you
meant to type “==”. Keep an eye out for this error.
Below and example of the working of the operators “+”, “==”, and
“!=” is detailed.

16/97
Operators on objects of type int and float
i+j is the sum of i and j. If i and j are both of type int, the result
is an int. If either is a float, the result is a float.
i-j is i minus j. If i and j are both of type int, the result is an
int. If either is a float, the result is a float.
i*j is the product of i and j. If i and j are both of type int, the
result is an int. If either is a float, the result is a float.
i//j is floor division. For example, the value of 6//2 is the int 3 and
the value of 6//4 is the int 1. The value is 1 because floor
division returns the quotient and ignores the remainder. If j=0,
an error occurs.
i/j is i divided by j. The / operator performs floating-point division.
For example, the value of 6/4 is 1.5. If j=0, an error occurs.
i%j is the remainder1 when the int i is divided by the int j.
i**j is i raised to the power j. If i and j are both of type int, the
result is an int. If either is a float, the result is a float.
1
typically pronounced “i mod j”, which is short for “i modulo j”. 17/97
Evaluating operators on objects of type int and float
Using an interactive window in VS Code

18/97
Operators on objects of type bool
and a and b is True if both a and b are True, and False otherwise.
or a or b is True if at least one of a or b is True, and False
otherwise.
not the expression not a is True if a is False, and False if a is
True.
Example in an interactive window in VS Code

19/97
Operators on objects of type str
Strings, i.e., objects of type str are used to represent characters.
Literals of type str can be written using either single or double
quotes, e.g., 'abc' or "abc".
The literal '123' denotes a string of three characters, not the number
abc.
Example in a VS Code interactive window

20/97
Operators overloading
The operator “+” is said to be overloaded because it has different
meanings depending upon the types of the objects to which it is
applied.
For example, the operator “+” means addition when applied to two
numbers and concatenation when applied to two strings.
The operator “*” is also overloaded. It means what you expect it to
mean when its operands are both numbers.
When applied to an int and a str, it is a repetition operator the
expression n*s, where n is an int and s is a str, evaluates to a str
with n repeats of s.
For example, the expression 2*'John' has the value JohnJohn.
There is a logic to this.
Just as the mathematical expression 3*2 is equivalent to 2+2+2, the
expression 3*'a' is equivalent to 'a'+'a'+'a'.

21/97
Object types and variables
The four basic object types in Python are integers, floating-point
numbers, booleans, and strings.
Variables provide a way to associate names with objects.
Consider the following code snippet
pi = 3
radius = 11
area = pi * ( radius **2)
radius = 14

The code first binds the names pi and radius to different objects of
type int. It then binds the name area to a third object of type int.
If the program then executes radius = 14, the name radius is
rebound to a different object of type int.
Last assignment has no effect on the value to which area is bound. It
is still bound to the object denoted by the expression 3*(11**2).
22/97
Assignment statements
An assignment statement associates the name to the left of the “=”
symbol with the object denoted by the expression to the right of the
“=” symbol.
An object can have one, more than one, or no name associated with it.
Python allows multiple assignment. For example, the statement
x, y = 2, 3

binds x to 2 and y to 3.
Python variable names are case-sensitive, e.g., Area and area are
different names.
They can contain uppercase and lowercase letters, digits2 , and the
special character “_” (underscore).
It is important to write programs so that are easy to read. The choice
of variable names plays an important role in enhancing readability.

2
In Python variable names cannot start with a digit 23/97
input() and variable assignment
The built-in function input() can be used to get input directly from
the user. The input() function takes a string as an argument and
displays it as a prompt in the terminal or shell.
The function then waits for the user to type something and press the
enter key. The line typed by the user is treated as a string and
becomes the value returned by the function.
Executing the code
name = input ( ' Enter your name : ')

will display the line


Enter your name :

in the console window.


If you then type George and press enter, the string 'George' will be
assigned to the variable name.
24/97
input() and variable assignment (ii)
Now consider the code
n = input ( ' Enter an int : ')
print ( type ( n ) )

This code will always print


< class ' str ' >

because input always returns an object of type str, even if the user
has entered something that looks like an integer.
For example, if the user had entered 3, n would be bound to the str
'3' not the int 3.
As a consecuence, the value of the expression n*4 would be '3333'
rather than 12.
Nevertheless, whenever a string is a valid literal of some type, a type
conversion can be applied to it.
25/97
Reserved words
In Python there are a few reserved words, sometimes called keywords,
that have built-in meanings and cannot be used as variable names.
These can be queried using the next code snippet.
import keyword

reserved_words = keyword . kwlist


print ( reserved_words )

Running above code in a VS Code interactive window results in

26/97
Reserved words (ii)
Clicking the “Variables” tab in the VS Code interactive window, this is

opens the “JUPYTER: VARIABLES” tab on the bottom of the window.


Clicking on the double square icon on the left of the
“reserved_words” variable opens a new editor displaying the values
as shown in the next slide.
27/97
Reserved words (iii)

It can be easy checked that there are 35 reserved words.


Notice also that the indexing starts at 0 in Python.
28/97
Next

Programs, Commands, and Definitions

Objects, Expressions, and Operators

Conditional Statements and Loops

Functions, Scoping, and Abstraction

Strings, Tuples, and Ranges

Lists and Mutability

Sets and Dictionaries


if and else statements
In Python, a conditional statement has the form
if boole an _e xp re ss ion :
# block of code

or
if boole an _e xp re ss ion :
# block of code
else :
# block of code

boolean_expression indicates that any expression that evaluates to


True or False can follow the reserved word if.
On the other hand, # block of code denotes that any sequence of
Python statements can follow else:.
Let’s see and example, a program that prints “Even” if the value of
the variable x is even and “Odd” otherwise.

29/97
if and else example
if x %2 == 0:
print ( ' Even ')
else :
print ( ' Odd ')

print ( ' Done with conditional ')

The expression x%2 == 0 evaluates to True when the remainder of x


divided by 2 is 0, and False otherwise.
Indentation is semantically meaningful in Python.
For example, if the last statement in the above code were indented, it
would be part of the block of code associated with the else, rather
than the block of code following the conditional statement.
Python is unusual in using indentation this way. Most other
programming languages use bracketing symbols to delineate blocks of
code, e.g., C encloses blocks in set braces, i.e., “{ }”.
30/97
Indentation and lines
An advantage of the Python approach is that it ensures that the
visual structure of a program is an accurate representation of its
semantic structure.
Because indentation is semantically important, the notion of a line is
also important.
A line of code that is too long to read easily can be broken into
multiple lines on the screen by ending each line on the screen, other
than the last one, with a backslash “\”. For example,
x = 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +\
2 2 22 2 2 2 2 2 2 2 2 3 3 3 2 2 22 2 2 2 22 +\
3333333333333333333333333333333

works properly.
Long lines can also be wrapped using Python’s implied line
continuation. This is done with bracketing, i.e., parentheses, square
brackets, and braces.
31/97
Nested conditionals
Returning to conditionals, when either the true block or the false
block of a conditional contains another conditional, the conditional
statements are said to be nested.
The following code contains nested conditionals in both branches of
the top-level if statement.

if x %2 == 0:
if x %3 == 0:
print ( ' Divisible by 2 and 3 ')
else :
print ( ' Divisible by 2 and not by 3 ')
elif x %3 == 0:
print ( ' Divisible by 3 and not by 2 ')

If x is divisible by both 2 and 3, it prints “Divisible by 2 and 3”.


If x is divisible by 2 but not by 3, it prints “Divisible by 2 and
not by 3”. And if x is divisible by 3 but not by 2, it prints
“Divisible by 3 and not by 2”.
32/97
elif statement
The elif statement in Python stands for “else if”.
It is used in conditional statements to evaluate multiple conditions
after an initial if statement.
When the condition of the if statement is not met, the elif
statement allows to check another condition.
The elif statement is optional and can be used multiple times in a
sequence following an initial if statement.
It provides an alternative check if the preceding condition(s) are not
true.
Remember, the elif statement is only evaluated if the preceding if
statement is false.
If the preceding if statement is true, the elif statement is skipped
entirely.

33/97
while loop
A while loop is a control flow statement that allows a block of code
to be executed repeatedly as long as a given condition is true.
It’s important to ensure that the condition in a while loop eventually
becomes False to avoid infinite loops.
Below snippet illustrates how a while loop works:
x = 0
while x < 5:
print ( x )
x += 1

In the above example, the variable x is initially set to 0. The while


loop checks if the value of x is less than 5.
If it is, the code block inside the loop is executed, which in this case is
printing the value of x and incrementing its value by 1.
This process continues until the value of x reaches 5, at which point
the condition x < 5 evaluates to False and the loop ends.
34/97
for loop
A for loop is also a control flow statement. It that allows to iterate
over a sequence of elements.
Here’s an example to illustrate how a for loop operates:
fruits = [ " apple " , " banana " , " cherry " ]
for fruit in fruits :
print ( fruit )

In this example, the fruits list contains three elements. The loop
iterates over each item in the fruits list, and for each item, it prints
the value of the fruit variable.

35/97
Python lists
Lists are Python built-in data structure used to store multiple items in
a single variable.
Lists are mutable, meaning they can be changed after they are
created, and they are ordered, which means the elements in a list have
a specific order.
A list in Python by enclosing a sequence of elements in square
brackets “[ ]”.
The elements in a list can be of any data type, such as integers,
strings, floats, or even other lists.
Lists can also contain a mix of different data types.
For instance, next code
my_list = [1 , 2 , 3 , " apple " , " banana " , 3.14]

creates a list named my_list which contains integers, strings, and a


float.
36/97
for loop using range()
A for loop can also iterate over a sequence of numbers. This can be
done by using the range() built-in function in conjunction with the
for loop.
For example, in below code snippet:
for i in range (5) :
print ( i )

the range(5) function generates a sequence of numbers from 0 to 4.


Recall that indexing in Python starts at 0.
The for loop iterates over each number in the sequence, and for each
number, it prints the value of i.
It’s important to note that the variable in a for loop, i in the last
example, can be named anything you want.
It’s a good practice to choose a meaningful name that represents the
nature of the elements in the sequence.
37/97
Next

Programs, Commands, and Definitions

Objects, Expressions, and Operators

Conditional Statements and Loops

Functions, Scoping, and Abstraction

Strings, Tuples, and Ranges

Lists and Mutability

Sets and Dictionaries


Functions
So far, we have introduced numbers, assignments, input/output,
comparisons, and looping constructs.
How powerful is this subset of Python? In a theoretical sense, it is as
powerful as needed, i.e., it is Turing complete.
This means that if a problem can be solved using computation, it can
be solved using only those linguistic mechanisms you have already
seen.
While any computation can, in principle, be implemented using only
these mechanisms, doing so is wildly impractical.
The more code a program contains, the more chance something can
go wrong, and the harder the code is to maintain.
Fortunately, Python provides several linguistic features that make it
relatively easy to generalize and reuse code.
The most important is the function, this is, the Pyhton definitions as
briefly introduced previously.
38/97
Defining a function
The def command in Python is used to define a function.
It is followed by the name of the function, a set of parentheses that
can optionally contain input parameters (a.k.a. arguments), and a
colon.
The body of the function is indented below the def statement. Here
is an example of a simple function definition:
def greet () :
print ( " Hello , world ! " )

greet ()

In this example, we define a function named greet that does not take
any input arguments.
The body of the function consists of a single line that prints “Hello,
world!”.
The function is called by using its name followed by parentheses.
39/97
Function with arguments
It is also possible to define functions that accept arguments.
Arguments are variables that are used to pass values into a function.
Here’s an example:
def greet_by_name ( name ) :
print ( f " Hello , { name }! " )

greet_by_name ( " Alice " )

In this example, the greet function takes a single parameter name.


When the function is called and passed the argument Alice, the
function will print “Hello, Alice!”.
The “f" "” within the print statement stands for formatted string
literals.
It is a feature that allows to embed expressions inside string literals
using curly braces “{ }”.
40/97
Returning values from a function
Functions can also return values using the return statement.
Below is a code snippet displaying this feature:
def multiply (a , b ) :
return a * b

result = multiply (2 , 3)
print ( result )
print ( f " The product of { x } and { y } is { multiply (x , y ) }. " )

This last function takes two arguments x and y, and returns their
product using the return statement.
For instance, above code calls the function with the arguments 2 and
3, then it assigns the returned value to the variable result.
Finally, the value of variable named result, and the string literal
explaining the calculation, are printed.

41/97
Default argument values
Python allows to specify default values for function arguments.
These default values are used when the caller does not provide a value
for that argument.
Next is shown an example.
def g r ee t _ w i t h _ d e f a u l t _v a l u e ( name = " world " ) :
print ( f " Hello , { name }! " )

g r ee t _ w i t h _ d e f a u l t _ v a l ue ()
g r ee t _ w i t h _ d e f a u l t _ v a l ue ( " Alice " )

In this last example, the greet_with_default_value function has a


default argument value of “world”.
When the function is called without any arguments, it will use the
default value and print “Hello, world!”.
However, if it is provided an argument like “Alice”, it will use that
value instead and print “Hello, Alice!”.
42/97
Variable number of arguments
Python makes easy for programmers to define their own functions
that accept a variable number of arguments.
The unpacking operator “*” allows a function to accept a variable
number of positional arguments.
For example,
def mean (* args ) :
# Assumes at least one argument and all arguments
are numbers
# Returns the mean of the arguments
tot = 0
for a in args :
tot += a
return tot / len ( args )

Note that the name following the “*” in the argument list need not
be args, it can be any name.

43/97
Function Scoping
Let’s consider the following code:
def f ( x ) : # x is the function argument name
y = 1
x = x + y
print ( 'x = ' , x )
return x
x = 3 # binding x value
y = 2
z = f ( x ) # value of x used when calling f
print ( 'z = ' , z )
print ( 'x = ' , x )
print ( 'y = ' , y )

When run in an interactive window this code prints

44/97
Scope working
Each function defines a new name space, also called a scope.
In tha above example, at the call of the f definition, the input
parameter x is locally bound to the value of x given in the context of
the function f.
When f is entered, a symbol table, called a stack frame, is created.
The names within f inner stack frame are y and x.
def f ( x ) : # x is the function argument name
y = 1
x = x + y
print ( 'x = ' , x )
return x

The input parameter x and the local variable y that are used in f
exist only within the scope of the definition of f.
The assignments in f have no effect on the bindings of the names
that exist outside the scope of f.
45/97
Scope working (ii)
The assignment statements
x = 3 # binding x value
y = 2
z = f ( x ) # value of x used when calling f

are outside the scope of f, and therefore these names are valid in an
outer stack frame.
The line of code z = f(x) first evaluates the expression f(x) by
invoking the function f with the input argument value to which x is
bound in the outer stack frame.
Then argument x value is set and nothing happens to the x varible in
the calling context, i.e., in the outer stack frame.
Note that the input argument and the variable outside the scope of f
have the same name x.
However they are not the same variable.
46/97
Lexical scoping
In Python, the scope of a name can be determined by looking at the
program text. This is called static or lexical scoping.
The following steps summarize this working:
i. At the outer level, a stack frame keeps track of all names defined
at that level and their current bindings.
x = 3 # binding x value
y = 2

ii. When a function is called, a new stack frame is created. It keeps


track of all names defined within the function, including the
input arguments, and their current bindings.
def f ( x ) : # x is the function argument name
y = 1

iii. When the function completes, its stack frame goes away.
Note that if a function is called from within the function body another
stack frame is created.
47/97
Function specification
A specification of a function defines a set of conditions between the
implementer of a function and those who will be writing programs
that use the function.
The users of a function are called its clients.
Aforementioned set of conditions can be thought of as a contract
containing two parts: (i.) assumptions; and (ii.) guarantees. Both are
described next.
Assumptions: they describe conditions that must be met by clients
of the function. Typically, they describe constraints on the input
parameters. Almost always, they specify the acceptable set of types
for each parameter, and not infrequently some constraints on the
value of one or more parameters.
Guarantees: these gather the conditions that must be met by the
function, provided it has been called in a way that satisfies the
assumptions.
48/97
Function specification example
def find_root (x , power , epsilon ) :
''' Assumes x and epsilon int or float , power an
int , epsilon > 0 and power >=1. Returns float y
such that y ** power is within epsilon of x .
If such a float does not exist , it returns None . '''
# Find interval containing answer
if x <0 and power %2 == 0:
return None
low = min ( -1 , x )
high = max (1 , x )
# Use bisection search
ans = ( high + low ) /2
while abs ( ans ** power - x ) >= epsilon :
if ans ** power < x :
low = ans
else :
high = ans
ans = ( high + low ) /2
return ans

49/97
Function specification example (ii)
The text between the triple quotation marks is called a docstring in
Python.
By convention, Python programmers use docstrings to provide
specifications of functions.
These docstrings can be accessed using the built-in function help.
Running last code in an interactive window and then calling
help(find_root) yields

50/97
Decomposition and abstraction
Functions are a way of creating computational elements that can be
thought of as primitives. As such, they provide decomposition and
abstraction.
Decomposition creates structure and allows to break a program into
parts that are reasonably self-contained and that may be reused in
different settings.
Abstraction hides detail and allows to use a piece of code as if it
were a black box, i.e., something whose interior details cannot be
seen, don’t need to be seen, and shouldn’t even want to be seen.
The essence of abstraction is preserving information that is relevant in
a given context, and forgetting information that is irrelevant in that
context.
The key to using abstraction effectively in programming is finding a
notion of relevance that is appropriate for both the builder of an
abstraction and the potential clients of the abstraction.
51/97
Code modularization and functions as objects
As we implement more complicated functionality, it is convenient to
split functions into multiple functions, each of which does one simple
thing.
A good rule of thumb is that if a function fits comfortably on a single
page, it probably doesn’t need to be subdivided to be easily
understood.
In Python, functions are first-class objects. That means they can be
treated like objects of any other type, e.g., int or list.
Functions have types, e.g., the expression type(abs) has the value
<type ’builtin_function_or_method’>.
They can appear in expressions, e.g., as the right-hand side of an
assignment statement or as an argument to a function; they can be
returned by functions; etc.
Using functions as arguments allows a style of coding called
higher-order programming.
52/97
Lambda expressions
Python supports the creation of anonymous functions, i.e., functions
that are not bound to a name, using the reserved word lambda.
Lambda functions are often used when i needed a quick one-line
function without defining a separate function using def.
They can take any number of arguments but can only have one
expression.
# General form of a lambda expression
lambda sequence of variable names : expression

Lambda functions are often used as arguments for higher-order


functions. They provide a concise and convenient way to define small,
one-time functions without the need for a formal function definition.
Lambda expressions can be useful in certain situations, however they
are typically limited in their complexity and are not meant to replace
regular named functions for more complex tasks.
53/97
Lambda function examples
Below code snippets show examples of two lambda functions.
# Lambda function with one argument
double = lambda x : x * 2
print ( double (5) ) # Output : 10

In the first example it is defined the lambda function double that


takes one argument x and returns x * 2. If double is called with the
argument 5, the output will be 10.
Let’s a see another example.
# Lambda function with multiple arguments
add = lambda x , y : x + y
print ( add (3 , 4) ) # Output : 7

In this latter example, the lambda function add is defined. It takes


two arguments x and y, and returns their sum. Calling this second
lambda function with arguments 3 and 4, will yield the output 7.
54/97
Next

Programs, Commands, and Definitions

Objects, Expressions, and Operators

Conditional Statements and Loops

Functions, Scoping, and Abstraction

Strings, Tuples, and Ranges

Lists and Mutability

Sets and Dictionaries


Structured types and methods
So far have dealt with three types of objects: int, float, and str.
The numeric types int and float are scalar types. Objects of these
types have no accessible internal structure.
In contrast, str can be thought of as a structured, or non-scalar,
type. Strings in Python are immutable, meaning that individual
characters within an existing string cannot be changed.
String methods can be used to manipulate strings, e.g., create new
strings with modified content. Methods3 are function-like objects.
They can be called with parameters, they can return values, and they
can have side effects.
Instead of putting the first argument inside parentheses following the
function name, the dot notation is used to place that argument before
the function name.
Many useful operations on built-in types are methods, and therefore
invoked using dot notation.
3
For now, think of methods as providing a peculiar syntax for a function call. 55/97
String manipulation: formatting
In the following, main examples of string manipulation will be
introduced.
# Formatting : Combining variables and strings .
name = " Alice "
age = 30
msg = " My name is {} and I am {} years old . " . format ( name
, age )
print ( mesg )
# Output : " My name is Alice and I am 30 years old ."

# f - strings : A more concise way to format strings .


name = " Bob "
age = 25
msg = f " My name is { name } and I am { age } years old . "
print ( msg )
# Output : " My name is Bob and I am 25 years old ."

The .format(name, age) is the dot way of calling method, i.e., the
function format belonging to the type str. str type
56/97
String manipulation: concatenation
Concatenation is the process of combining two or more strings
together to create a single, longer string.
In Python, concatenation is a fundamental string operation that
allows you to join strings end-to-end.
It’s often used to create new strings by combining existing ones.
# Concatenation : Joining two or more strings together .
str1 = " Hello "
str2 = " World "
result = str1 + " " + str2
print ( result ) # Output : " Hello World "

The operator “+” is said to be overloaded because it has different


meanings depending upon the types of the objects to which it is
applied.
In the above code “+” means concatenation because it is applied to
two strings.
57/97
String manipulation: indexing and slicing
Strings are sequences of characters, and each character in a string has
a unique index, starting from 0 for the first character.
Negative indices count from the end of the string.
Slicing allows to extract a portion of a string using a range of indices.
The general syntax for slicing is string_name[start:end], where
start is inclusive and end is exclusive.
# Indexing and slicing .
text = " Hello , World ! "
print ( text [0]) # Output : "H"
print ( text [7]) # Output : "W"
print ( text [ -1]) # Output : "!"
print ( text [ -2]) # Output : "d"

print ( text [0:5]) # Output : " Hello "


print ( text [7:]) # Output : " World !"
print ( text [:5]) # Output : " Hello "
print ( text [ -6:]) # Output : " World !"

58/97
String manipulation: slicing (ii)
If start or end is not provided, it defaults to the beginning or end of
the string, respectively.
In addition, it is also possible to specify a step value in slicing to skip
characters in between.
The syntax is string_name[start:end:step].
# Slicing with a step value .
numbers = " 0123456789 "

print ( numbers [::2]) # Output : "02468" ( even - indexes )


print ( numbers [1::2]) # Output : "13579" ( odd - indexes )
print ( numbers [:: -1]) # Output : "9876543210" ( reversed )

Recall that strings in Python are immutable, this is, individual


characters within an existing string cannot be changed.
As a consequence, slicing doesn’t modify the original string, it creates
a new string containing the sliced portion.
59/97
String manipulation: methods
Here are some examples of str type useful methods.
# Converting strings to uppercase or lowercase .
text = " Python is Fun "
uppercase = text . upper ()
lowercase = text . lower ()
print ( uppercase ) # Output : " PYTHON IS FUN "
print ( lowercase ) # Output : " python is fun "

# Removing leading and trailing whitespace .


text = " Some spaces here "
stripped_text = text . strip ()
print ( stripped_text )
# Output : " Some spaces here "

# Replacing occurrences of one substring with another .


sentence = " I like cats , but I prefer dogs . "
new_sentence = sentence . replace ( " cats " , " rabbits " )
print ( new_sentence )
# Output : " I like rabbits , but I prefer dogs ."

60/97
String manipulation: more methods
s.count(s1) counts how many times the string s1 occurs in s.
s.find(s1) returns the index of the first occurrence of the substring
s1 in s, and -1 if s1 is not in s.
s.rfind(s1) same as find, but starts from the end of s (the “r” in
rfind stands for reverse).
s.index(s1) same as find, but raises an exception if s1 is not in s.
s.rindex(s1) same as index, but starts from the end of the string s.
s.replace(old, new) replaces all occurrences of the string old in s
with the string new.
s.split(d) splits s using d as a delimiter, and returns a list of
substrings of s. If d is omitted, the substrings are separated by
arbitrary strings of whitespace characters. For example,
' He plays basketball '. split ( ' ')
# Output : [ ' He ', ' plays ', ' basketball ']

61/97
Tuples
Like strings, tuples are immutable ordered sequences of elements. The
difference is that the elements of a tuple need not be characters.
The individual elements can be of any type, and need not be of the
same type as each other.
Literals of type tuple are written by enclosing a comma separated list
of elements within parentheses.
For example,
t1 = ()
t2 = (1 , ' two ' , 3)
print ( t1 )
print ( t2 )

yields
()
(1 , ' two ' , 3)

62/97
Tuple manipulation: repetition
Recall that parentheses are used to group expressions, therefore (1) is
merely a verbose way to write the integer 1.
This is, (1,) denotes the singleton tuple containing the value 1. It is
a very common mistake to omit the comma.
Repetition can be used on tuples, e.g., the expression 3*('a', 2)
evaluates to ('a', 2, 'a', 2, 'a', 2).
Like strings, tuples can be concatenated, indexed, and sliced.
Consider the next code
t1 = (1 , ' two ' , 3)
t2 = ( t1 , 3.25)

The second assignment statement binds the name t2 to a tuple that


contains the tuple to which t1 is bound and the number 3.25.
This is possible because a tuple, like everything else in Python, is an
object, so tuples can contain tuples.
63/97
Tuple manipulation: concatenation, indexing, and slicing
Therefore, the following statements
print ( t2 )
print (( t1 + t2 ) )
print (( t1 + t2 ) [3])
print (( t1 + t2 ) [2:5])

produce the output,


((1 , ' two ' , 3) , 3.25)
(1 , ' two ' , 3 , (1 , ' two ' , 3) , 3.25)
(1 , ' two ' , 3)
(3 , (1 , ' two ' , 3) , 3.25)

The third statement selects and prints the fourth element of the
concatenated tuple. Recall that, as always in Python, indexing starts
at 0.
The last statement creates and prints a slice of the same
concatenated tuple used in the previous statement.
64/97
Iterating over the elements of a tuple
A for statement can be used to iterate over the elements of a tuple.
And the in operator can be used to test if a tuple contains a specific
value.
For example, the following code
def intersect ( t1 , t2 ) :
""" Assumes t1 and t2 are tuples .
Returns a tuple containing elements that are in both
t1 and t2 . """
result = ()
for e in t1 :
if e in t2 :
result += (e ,)
return result

print ( intersect ((1 , 'a ' , 2) , ( 'b ' , 2 , 'a ') ) )

yields
( 'a ' , 2)
65/97
Ranges
The function range() generates a sequence of integers and produces
an object of type range.
Like strings and tuples, objects of type range are immutable.
For instance, the next code snippet
for e in range ( -4 ,3 ,2) :
print (e , type ( e ) )

produces
-4 < class ' int ' >
-2 < class ' int ' >
0 < class ' int ' >
2 < class ' int ' >

All of the operations on tuples are also available for ranges, except for
concatenation and repetition.
For example, range(10)[2:6][2] evaluates to 4.
66/97
Comparing ranges
The “==” operator is used to compare objects of type range, and it
returns True if the two ranges represent the same sequence of
integers. For example,
range (0 , 7 , 2) == range (0 , 8 , 2)
range (0 , 7 , 2) == range (6 , -1 , -2)

evaluate to
True
False

Because though the two ranges contain the same integers, they occur
in a different order.
Unlike objects of type tuple, the amount of space occupied by an
object of type range is not proportional to its length.
Because a range is fully defined by its start, stop, and step values, it
can be stored in a small amount of space.
67/97
Iterables and ranges
The most common use of range is in for loops, but objects of type
range can be used anywhere a sequence of integers can be used.
In Python 3, range is a special case of an iterable object. All iterable
types have a method (i.e., simply a function that is invoked using dot
notation) “ iter ” that returns an object of type iterator.
The iterator can then be used in a for loop to return a sequence of
objects, one at a time.
For example, tuples are iterable, and the statement
for elem in (1 , 'a ' , 2 , (3 , 4) ) :

creates an iterator returning the elements of the tuple one at a time.


Python has many built-in iterable types, including strings, lists, and
dictionaries.
Many built-in functions operate on iterables, among the more useful
are sum(), min(), and max().
68/97
Next

Programs, Commands, and Definitions

Objects, Expressions, and Operators

Conditional Statements and Loops

Functions, Scoping, and Abstraction

Strings, Tuples, and Ranges

Lists and Mutability

Sets and Dictionaries


Lists
A list is an ordered sequence of values, where each value is identified
by an index. The syntax for expressing literals of type list are the
square brackets. The empty list is written as “[]”, and singleton lists
are written without a comma before the closing bracket.
Lists are iterables, as a consequence, a for statement can be used to
iterate over the elements in list. In addition, it is possible index into
lists and slice lists, just like the tuples.
For example, the code
L1 = [1 , 2 , 3]
L2 = L1 [ -1:: -1]
for i in range ( len ( L1 ) ) :
print (i , L1 [ i ] , L2 [ i ] , L1 [ i ]* L2 [ i ])

prints
0 1 3 3
1 2 2 4
2 3 1 3
69/97
Mutability
Tuples and strings are immutable, lists differ from both in one hugely
important feature: lists are mutable.
Recall that in Python, everything is an object, i.e., a reusable piece of
code that contains both data (attributes) and functions (methods)
that operate on that data.
Many operators can be used to create objects of immutable types,
and variables can be bound to objects of these types.
Objects of immutable types cannot be modified after they are created.
On the other hand, objects of mutable types can be modified after
they are created.
The distinction between mutating an object and assigning an object
to a variable may, at first, appear subtle.
However, considering that “in Python a variable is merely a name,
i.e., a label that can be attached to an object”, and the next
examples, will bring some clarity.
70/97
list objects equality
When the statements
Techs = [ ' MIT ' , ' Caltech ']
Ivys = [ ' Harvard ' , ' Yale ' , ' Brown ']

are executed, the interpreter creates two new lists and binds the
appropriate variables to them.
Below assignment statements running
Univs0 = [ Techs , Ivys ]
Univs1 = [[ ' MIT ' , ' Caltech '] , [ ' Harvard ' , ' Yale ' , ' Brown ' ]]

also create new lists and bind variables to them. The elements of
these lists are themselves lists.
It might appear that Univs0 and Univs1 are bound to the same
value. Nevertheless, they are bound to different objects.
This can be verified using the built-in Python function id, which
returns a unique integer identifier for an object, or the “is” operator.
71/97
Testing list objects equality
After running the last assignments, the next code
print ( ' Univs0 = ' , Univs0 )
print ( ' Univs1 = ' , Univs1 )
print ( Univs0 == Univs1 ) # value equality
print ( id ( Univs0 ) == id ( Univs1 ) ) # object equality via ' id '
print ( Univs0 is Univs1 ) # object equality using ' is '
print ( ' Id of Univs0 = ' , id ( Univs0 ) )
print ( ' Id of Univs1 = ' , id ( Univs1 ) )

prints
Univs0 = [[ ' MIT ' , ' Caltech '] , [ ' Harvard ' , ' Yale ' , ' Brown ' ]]
Univs1 = [[ ' MIT ' , ' Caltech '] , [ ' Harvard ' , ' Yale ' , ' Brown ' ]]
True
False
False
Id of Univs0 = 1988839960512
Id of Univs1 = 1988839955072

Therefore Univs0 and Univs1 are bound to different objects.


72/97
list objects bindings

Techs ['MIT','Caltech']

Univs0 [ , ]

Ivys ['Harvard','Yale','Brown']

['MIT','Caltech']

Univs1 [ , ]

['Harvard','Yale','Brown']

Figure 1: Techs, Ivys, Univs0, and Univs1 bindings.


73/97
list objects bindings
The elements of Univs0 are not copies of the lists to which Techs
and Ivys are bound, but are rather the lists themselves, as depicted
in Figure 1.
The elements of Univs1 are lists that contain the same elements as
the lists in Univs0, but they are not the same lists.
This can be seen by running the code
print ( ' Ids of Univs0 [0] and Univs0 [1] ' , id ( Univs0 [0]) ,
id ( Univs0 [1]) )
print ( ' Ids of Univs1 [0] and Univs1 [1] ' , id ( Univs1 [0]) ,
id ( Univs1 [1]) )

which prints
Ids of Univs0 [0] and Univs0 [1] 1988839955328
1988839933760
Ids of Univs1 [0] and Univs1 [1] 1988839611008
1988839425728

74/97
Mutating a list object
The difference between value and object equality matters because lists
are mutable.
The append method for lists mutates the existing list by adding a new
element to the end of it.
If in the last example the next code is ran
Techs . append ( ' RPI ')

then the existing list, Techs, has been mutated by adding a new
element, the string ’RPI’, to the end of it.
The append method for lists has a side effect on Univs0.
As aforementioned, rather than create a new list, it mutates the list
Techs, by adding a new element to the end of it. As a consequence,
element Univs0[0] has also been mutated.
Next Figure 2 depicts the state of the computation after append is
executed.
75/97
list objects and mutability

Techs ['MIT','Caltech','RPI']

Univs0 [ , ]

Ivys ['Harvard','Yale','Brown']

['MIT','Caltech']

Univs1 [ , ]

['Harvard','Yale','Brown']

Figure 2: Techs, Ivys, Univs0, and Univs1 after append is executed.


76/97
Aliasing
The object to which Univs0 is bound still contains the same two lists,
but the contents of one of those lists has been changed. Consequently,
print ( ' Univs0 = ' , Univs0 )

yields
Univs0 = [[ ' MIT ' , ' Caltech ' , ' RPI '] , [ ' Harvard ' , ' Yale ' ,
' Brown ' ]]

What we have here is called aliasing. There are two distinct paths to
the same list object.
One path is through the variable Techs and the other through the
first element of the list object to which Univs0 is bound.
We can mutate the object via either path, and the effect of the
mutation will be visible through both paths.
This can be convenient, nevertheless unintentional aliasing leads to
programming errors that are often enormously hard to track down.
77/97
List concatenation
If the goal is to add the elements of one list into another list, then list
concatenation must be performed.
This can be done by means of the “+” operator, or the extend
method. e.g.,
L1 , L2 = [1 ,2 ,3] , [4 ,5 ,6]
L3 = L1 + L2
print ( ' L3 = ' , L3 )
L1 . extend ( L2 )
print ( ' L1 = ' , L1 )
L1 . append ( L2 )
print ( ' L1 = ' , L1 )

will print
L3 = [1 , 2 , 3 , 4 , 5 , 6]
L1 = [1 , 2 , 3 , 4 , 5 , 6]
L1 = [1 , 2 , 3 , 4 , 5 , 6 , [4 , 5 , 6]]

The operator “+” does not have a side effect, it creates a new list and
returns it. In contrast, extend and append each mutate L1. 78/97
list object methods
Given a list L, below are summarized main list type methods.
L.append(e): adds the object e to the end of L.
L.count(e): returns the number of times that e occurs in L.
L.insert(i,e): inserts the object e into L at index i.
L.extend(L1): adds the items in list L1 to the end of L.
L.remove(e): deletes the first occurrence of e from L
L.index(e): returns the index of the first occurrence of e in L, raises
an exception if e is not in L.
L.pop(i): removes and returns the item at index i in L, i default
value is -1, i.e., it removes and returns the last element of L.
L.sort(): sorts the elements of L in ascending order.
L.reverse(): reverses the order of the elements in L.
Note that all of these, except count and index, mutate the list.
79/97
Iterating and mutating
Iterating over a list and mutating it is not a good practice, e.g., the
next code
L4 , iter = [1 ,2 ,3] , 0
for e in L4 :
iter += 1
print ( f " iteration = { iter } , L1 = { L4 } , e = { e } " )
L4 . remove ( e )
print ( f " L4 = { L4 } ( after removing e = { e }) " )
print (5* " -" + " iteration finished " + 5* " -" )

will print
L4 = [2 , 3] ( after removing e = 1)
----- iteration finished -----
iteration = 2 , L1 = [2 , 3] , e = 3
L4 = [2] ( after removing e = 3)
----- iteration finished -----

One way to avoid this kind of problem is cloning, i.e., make a copy of,
the list before iterating it.
80/97
Cloning
Cloning a list can be done by means of slicing. If in above code the
second line (i.e., the one containing the for loop) is replaced by
for e in L4 [:]:

will print
iteration = 1 , L1 = [1 , 2 , 3] , e = 1
L4 = [2 , 3] ( after removing e = 1)
----- iteration finished -----
iteration = 2 , L1 = [2 , 3] , e = 2
L4 = [3] ( after removing e = 2)
----- iteration finished -----
iteration = 3 , L1 = [3] , e = 3
L4 = [] ( after removing e = 3)
----- iteration finished -----

as expected.
Slicing is not the only way to clone lists in Python, the expression
L.copy() has the same value as L[:].
81/97
List comprehension
List comprehension provides a concise way to apply an operation to
the sequence values provided by iterating over an iterable value.
It creates a new list in which each element is the result of applying a
given operation to a value from an iterable (e.g., the elements in
another list). It is an expression of the form
[ expr for elem in iterable if test ]

For example,
[ e **2 for e in range (6) ]
[ e **2 for e in range (8) if e %2 == 0]
[ x **2 for x in [2 , 'a ' , 3 , 4.0] if type ( x ) == int ]

evaluates respectively to
[0 , 1 , 4 , 9 , 16 , 25]
[0 , 4 , 16 , 36]
[4 , 9]

82/97
Higher-order operations on lists
Higher-order programming can be convenient with lists, as shown in
next example
def apply_to_each (L , f ) :
''' Assumes 'L ' is a list , 'f ' a function . Mutates
'L ' by replacing each element , 'e ', by 'f ( e ) '. '''
for i in range ( len ( L ) ) :
L [ i ] = f ( L [ i ])

L = [1 , -2 , 3.33]
print ( 'L = ' , L )
print ( ' Apply " abs " to each element of L ')
apply_to_each (L , abs )
print ( 'L = ' , L )
print ( ' Apply " int " to each element of ' , L )
apply_to_each (L , int )
print ( 'L = ' , L )
print ( ' Apply squaring to each element of ' , L )
apply_to_each (L , lambda x : x **2)
print ( 'L = ' , L )

83/97
Higher-order operations on lists (ii)
Executing last code snippet yields
L = [1 , -2 , 3.33]
Apply " abs " to each element of L
L = [1 , 2 , 3.33]
Apply " int " to each element of [1 , 2 , 3.33]
L = [1 , 2 , 3]
Apply squaring to each element of [1 , 2 , 3]
L = [1 , 4 , 9]

Function apply_to_each is called higher-order because it has an


argument that is itself a function.
The first time it is called, it mutates L by applying the unary built-in
function abs to each element.
The second time it is called, it applies a type conversion to each
element.
And the third time it is called, it replaces each element by the result
of applying a function defined using a lambda function.
84/97
Next

Programs, Commands, and Definitions

Objects, Expressions, and Operators

Conditional Statements and Loops

Functions, Scoping, and Abstraction

Strings, Tuples, and Ranges

Lists and Mutability

Sets and Dictionaries


Sets
Sets are similar to the notion of a set in mathematics in that they are
unordered collections of unique elements.
They are denoted using what programmers call curly braces and
mathematicians call set braces, i.e., “{ }”.
For example,
football_teams = { ' Real Madrid ' , ' Manchester City '}

creates a set type object and binds it to football_teams variable.


Since the elements of a set are unordered, attempting to index into a
set, e.g., evaluating
football_teams [0]

generates a runtime error.


A for statement can be used to iterate over the elements of a set,
but unlike the other collection types we have seen, the order in which
the elements are produced is undefined.
85/97
Mutability
Like lists, sets are mutable. A single element can be added to a set
using the add method, and multiple elements can be added by passing
a collection of elements (e.g., a list) to the update method.
For example, the code
football_teams . add ( ' Chelsea ')
print ( football_teams )
football_teams . update ([ ' Bayern Munchen ' , ' Liverpool ' ])
print ( football_teams )

prints
{ ' Manchester City ' , ' Real Madrid ' , ' Chelsea '}
{ ' Chelsea ' , ' Manchester City ' , ' Real Madrid ' , ' Liverpool
' , ' Bayern Munchen '}

The order in which the elements appear is not defined by the


language, so it is likely that a different result will be displayed when
the above code is executed.
86/97
Set manipulation
Elements can be removed from a set using the remove method, which
raises an error if the element is not in the set. Besides, the discard
method, which does not raise an error if the element is not in the set,
can be used to remove an element.
Membership in a set can be tested using the in operator. For example,
' Chelsea ' in football_teams

returns True.
The binary methods union, intersection, difference, and
issubset have their usual mathematical meanings. In addition, there
are infix operators for many of these methods: “|” for union, “&” for
intersect, “-” for difference, “<=” for subset, and “>=” for superset.
Using these operators makes code easier to read, for example
print ({ ' Real Madrid ' , ' Liverpool ' }. issubset (
football_teams ) )
print ({ ' Real Madrid ' , ' Liverpool '} <= baseball_teams )

87/97
Hashable objects
Not all types of objects can be elements of sets. All objects in a set
must be hashable. An object is hashable if it has:
i. A “ hash ” method that maps the object of the type to an
int, and the value returned by “ hash ” does not change
during the lifetime of the object.
ii. An “ eq ” method that is used to compare it for equality to
other objects.
All objects of Python’s scalar immutable types are hashable, and no
object of Python’s built-in mutable types is hashable.
An object of a non-scalar immutable type (e.g., a tuple) is hashable if
all of its elements are hashable.
For example, strings are immutable and hashable, however, if it
contains mutable objects, such as lists or dictionaries, it will not be
hashable.
The hash() function returns a unique hash value for a given object.
88/97
Set objects application
Although set theory is a cornerstone of mathematics and also finance
theory, there are not too many practical applications for set objects.
One application of set objects is to get rid of duplicates in a list object.
The following example is taken from Chapter 4 of Hilpisch (2014).
import random
random . seed (3.14159) # Set the seed
L = [ random . randint (0 , 10) for i in range (1000) ]
# 1 ,000 random integers between 0 and 10
print ( len ( L ) ) # number of elements in L
print ( L [:15]) # first elements in L
S = set ( L )
print ( S )

Last code snippet when executed yields


1000
[9 , 7 , 10 , 7 , 5 , 7 , 2 , 7 , 4 , 2 , 5 , 9 , 2 , 0 , 6]
{0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10}

89/97
Dictionaries
Dictionaries are one of the great things about Python. They greatly
reduce the difficulty of writing a variety of programs.
Objects of type dict, short for dictionary, are like lists except that we
index them using keys rather than integers. Any hashable object can
be used as a key.
A dictionary can be regarded as a a set of key/value pairs. Literals of
type dict are enclosed in curly braces and each element is written as a
key followed by a colon followed by a value.
For example, the code,
month_numbers = { ' Jan ':1 , ' Feb ':2 , ' Mar ':3 , ' Apr ':4 ,
' May ':5 , 1: ' Jan ' , 2: ' Feb ' , 3: ' Mar ' ,
4: ' Apr ' , 5: ' May '}

creates a dict object and binds it to month_numbers.


The entries in a dict cannot be accessed using an index. Whether a
key is defined in a dictionary can be tested using the in operator.
90/97
Handling a dict object
After creating month_numbers, the statements
print ( month_numbers )
print ( month_numbers [1])
print ( ' The third month is ' + month_numbers [3])
dist = month_numbers [ ' Apr '] - month_numbers [ ' Jan ']
print ( ' Apr and Jan are ' , dist , ' months apart ')

will result in
{ ' Jan ': 1 , ' Feb ': 2 , ' Mar ': 3 , ' Apr ': 4 , ' May ': 5 ,
1: ' Jan ' , 2: ' Feb ' , 3: ' Mar ' , 4: ' Apr ' , 5: ' May '}
Jan
The third month is Mar
Apr and Jan are 3 months apart

As aforementioned, the entries cannot be accessed using an index.


That’s why month_numbers[1] unambiguously refers to the entry
with the key 1 rather than the second entry.
Therefore print(month_numbers[1]) yields Jan.
91/97
Modifying and adding values
Like lists, dictionaries are mutable. Modifying an existing value in a
dictionary and adding a new key-value pair to a dictionary are both
performed using assignment statements.
The name of the dictionary, along with the key enclosed in square
brackets, is placed to the left of the assignment operator and the
value to associate with the key is placed to the right of the
assignment operator.
If the key is already present in the dictionary then the assignment
statement will replace the key’s current value with the value to the
right of the assignment operator.
If the key is not already present in the dictionary then a new key-value
pair is added to it.
These operations are demonstrated in the following code snippet.
month_numbers [ ' June '] = 6 # add a new key - value pair
month_numbers [ ' May '] = 'V ' # change an entry

92/97
Iterating over a dictionary entries
There are multiple ways to use a for statement to iterate over the
entries in a dictionary.
If d is a dictionary, a loop of the form for k in d iterates over the
keys of d.
The order in which the keys are chosen is the order in which the keys
were inserted in the dictionary.
For example,
capitals = { ' France ': ' Paris ' , ' Italy ': ' Rome ' ,
' Japan ': ' Kyoto '}
for key in capitals :
print ( ' The capital of ' , key , ' is ' , capitals [ key ])

prints
The capital of France is Paris
The capital of Italy is Rome
The capital of Japan is Kyoto

93/97
Iterating using values method
In addition, to iterate over the values in a dictionary, the method
values can be used.
For example,
cities = []
for val in capitals . values () :
cities . append ( val )
print ( cities , ' is a list of capital cities ')

prints
[ ' Paris ' , ' Rome ' , ' Kyoto '] is a list of capital cities

The method values returns an object of type dict_values.


This is an example of a view object.
A view object is dynamic in that if the object with which it is
associated changes, the change is visible through the view object, i.e.,
it is “linked to the parent object”.
94/97
Methods keys and items
Similarly, the method keys returns a view object of type dict_keys.
View objects can be converted to lists, for example, the command
list(capitals.values()) returns a list of the values in capitals.
To iterate over key/value pairs, the method items which returns a
view object of type dict_items, is used.
Each element of an object of type dict_items is a tuple of a key and
its associated value.
For example, the code
for key , val in capitals . items () :
print ( val , ' is the capital of ' , key )

prints
Paris is the capital of France
Rome is the capital of Italy
Kyoto is the capital of Japan

It is often convenient to use tuples as keys.


95/97
dict object methods
Given a dictionary d, below are summarized some of the more useful
operations on dictionaries.
len(d): returns the number of items in d.
d.keys(): returns a view of the keys in d.
d.values(): returns a view of the values in d.
d.items(): returns a view of the (key, value) pairs in d.
d.update(kv): updates d with the (key, value) pairs in kv,
overwriting existing keys.
k in d: returns True if key k is in d.
d[k]: returns the item in d with key k.
d.get(k,v): returns d[k] if key k is in d, and v otherwise.
d[k] = v: associates the value v with the key k in d. If there is
already a value associated with k, that value is replaced.
del d[k]: removes the key k from d.
96/97
Dictionary comprehension
Dictionary comprehension is similar to list comprehension, the general
form is
{ key : value for id1 , id2 in iterable if test }

The key difference is that it uses two values to create each element of
the dictionary.
In addition, it allows, but does not require, the iterable to return two
values at a time.
Consider a dictionary mapping some decimal digits to English words:
num_to_word = {1: ' one ' , 2: ' two ' , 3: ' three ' ,
4: ' four ' , 10: ' ten '}

A dictionary that maps words to digits, allowing only single digits, can
be easily produced by using dictionary comprehension, i.e.,
word_to_num = { w : d for d , w in num_to_word . items ()
if d < 10}

97/97
References
Guttag, John V. (2021). Introduction to Computation and
Programming Using Python. with Application to Computational
Modeling and Understanding Data. Third Edition. Cambridge,
MA: The MIT Press.
Hilpisch, Yves (2014). Python for Finance. Analyze Big
Financial Data. Ed. by O’Reilly. Beijing.

You might also like