1_PythonBasics
1_PythonBasics
Daniel Arrieta
[email protected]
IE University
3/97
Next
7/97
Definition example (ii)
To use this function, we can call it like this:
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
13/97
Next
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 : ')
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
26/97
Reserved words (ii)
Clicking the “Variables” tab in the VS Code interactive window, this is
or
if boole an _e xp re ss ion :
# block of code
else :
# block of code
29/97
if and else example
if x %2 == 0:
print ( ' Even ')
else :
print ( ' Odd ')
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 ')
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 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]
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 }! " )
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 " )
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 )
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
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
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 "
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 "
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 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
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) ) :
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
Techs ['MIT','Caltech']
Univs0 [ , ]
Ivys ['Harvard','Yale','Brown']
['MIT','Caltech']
Univs1 [ , ]
['Harvard','Yale','Brown']
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']
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]
prints
{ ' Manchester City ' , ' Real Madrid ' , ' Chelsea '}
{ ' Chelsea ' , ' Manchester City ' , ' Real Madrid ' , ' Liverpool
' , ' Bayern Munchen '}
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 )
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 '}
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
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
prints
Paris is the capital of France
Rome is the capital of Italy
Kyoto is the capital of Japan
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.