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

Introduction To Python Course Manual 65636f2ca569c

The document provides an overview of an introductory training manual for learning Python for financial services. It outlines that the course will introduce Python fundamentals and common third-party libraries for tasks like numerical computations, data manipulation, and charting. The content is designed for beginners and will cover topics such as data types, control flow, functions, classes, and data acquisition. Installation of Python and the use of virtual environments are also discussed.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views

Introduction To Python Course Manual 65636f2ca569c

The document provides an overview of an introductory training manual for learning Python for financial services. It outlines that the course will introduce Python fundamentals and common third-party libraries for tasks like numerical computations, data manipulation, and charting. The content is designed for beginners and will cover topics such as data types, control flow, functions, classes, and data acquisition. Installation of Python and the use of virtual environments are also discussed.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 456

Wall Street Prep Training Manual

Intro to Python for


Financial Services

v W W W. W A L L S T R E E T P R E P. C O M
Intro to Python for Financial Services

Course Overview

1 2
FooterLicensed to Kevin Romanteau. Email address: [email protected]
What this course is about…

à introduction to Python from first principles

à you will have the knowledge and understanding to learn more on your own

à get insights into 3rd party Python libraries in general

à learn the basics of some common 3rd Party libraries

à numerical computations

à manipulating data sets

à charting

3
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Content Overview
à installing and running Python

à basic principles numerical types (integers, floats, booleans)


variables
operators (arithmetic, logical, boolean)

à control flow conditional execution


iteration (iterables, iterators, loops)
exception handling

à advanced data types sequence types (lists, tuples, strings)


dictionaries and sets
dates and times
decimals

4
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Content Overview
à functional programming functions
higher-order functions
closures
decorators

à object-oriented programming custom classes


methods
properties

à data acquisition CSV


JSON
Using REST APIs

5
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Content Overview

à 3rd party Libraries pytz


dateutil
(http) requests
numpy
pandas
matplotlib

6
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Prerequisites
à Windows, Mac
à needs to run Python 3.6+ (recommend at least 3.8/3.9 or higher)
Windows 10 Mac 10.9 and higher
à Linux – you'll need to find/use installation instructions

à some familiarity with Terminal (Mac/Linux) / Command Prompt (Windows)


à will be needed to install and run Python
à just the basics à how to open/terminate a shell
à change directory
à list contents of a directory
à create a directory
à no prior Python knowledge needed
à prior programming knowledge helpful, but not required
7
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Course Structure and Materials
à each topic is organized in two parts
à lecture video sit back , watch, take notes if you like
à coding video lean in and code along – pause video, rewind, type code!

à exercises
à each section (except installation section) has a set of exercises
à make sure you are confident before moving on to the next section

à downloadable course materials


à all coding videos have an accompanying Jupyter Notebook / resources
à contains all the code we do in the code video
à contains any required extras (such as data sets)
à notebooks are fully annotated
8
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Intro to Python for Financial Services

Installing and
Running Python

2 9
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à what is Python?
à language vs implementation

à the canonical, or reference implementation of Python

à installing Python
à side by side versions of Python

à virtual environments

à installing 3rd party libraries

à running Python code


à interactive mode
à script mode

10
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Python

11
FooterLicensed to Kevin Romanteau. Email address: [email protected]
A bit of history…

à created by Guido van Rossum in 1989 while working at CWI


(National Research Institute for Mathematics and Computer Science, Netherlands)
à became a community driven effort, overseen by Guido
à who became the BDFL (benevolent dictator for life) (stepped down in 2018)
à many developers ("core" developers) have contributed over the years

à was named after the British comedy group Monty Python


(who says developers don't have a sense of humour!)

à still actively developed today


Python 2.0 released in 2000 à last release was 2.7 (end of life April 2020)
Python 3.0 released in 2008 à 3.9 released in October 2020

12
FooterLicensed to Kevin Romanteau. Email address: [email protected]
What is Python?
à Python is a language, not an application
à there are many implementations of Python
CPython
PyPy
à even compilers that "translate" Python code to other languages
IronPython à .NET
Jython à Java
Cython à C/C++

à the "reference" Python implementation is CPython

13
FooterLicensed to Kevin Romanteau. Email address: [email protected]
CPython
à reference implementation à https://www.python.org
à most widely used distribution of Python
à open source à written in C https://github.com/python/cpython
à includes the standard library
à a collection of additional functionality that goes beyond
just the Python language
à written in C and Python

à this implementation of Python and the standard library is the "official"


implementation

à many platforms Linux, Windows, Mac OS, iOS, Android, PlayStation, Xbox,…

14
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Installing Python

15
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Installing CPython
à CPython is basically a bunch of files, located in some directory on your
computer
à one of those files is an executable that is used to run Python code
files or an interactive shell
à entire standard library is also included in these files

à to "install" CPython you simply copy these files into a directory

à possible to have multiple versions of CPython on the same computer


à just install the files in different directories
à call the desired Python executable

16
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Where to find installation packages

à https://www.python.org

à click on Downloads à all releases

17
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à choose which version you want and what OS you are on

à or use default (which should recognize your OS)

18
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à two videos included in this course

à Windows Installation

à Mac Installation
(Linux installation is same as Mac, just download for Linux OS)

à jump directly to your specific platform video

use Python 3.8.1 or higher for this course

19
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Virtual Environments

20
FooterLicensed to Kevin Romanteau. Email address: [email protected]
3rd Party Libraries
à many 3rd party libraries exist
à add-ons to Python for more specialized functionality
à a bunch of files
à that get added to your Python "installation"

à 3rd party libraries often rely on other 3rd party libraries


à or might have specific releases for specific Python versions

à this can create conflicts!


my_app_1 à some_lib_1.0 (breaks with some_lib_1.1 and higher)
my_app_2 à some_lib_1.1 (breaks with some_lib_1.0 and lower)

21
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Solution
à since Python is just a directory of files
à create two copies of this directory
/usr/user1/python3.9-ENV1/ /usr/user1/python3.9-ENV2/
install some_lib_1.0 in here install some_lib_1.1 in here

à run your apps/shell using the appropriate Python directory

à /Users/user1/python3.9-ENV1/bin/python my_app.py

à /Users/user1/python3.9-ENV2/bin/python my_other_app.py

à typing this long path is tedious


à add to PATH environment variable (tells OS where to look for executables)

22
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Solution
/usr/user1/python3.9-ENV1/
à add /usr/user1/python3.9-ENV1/ to (front of) PATH
à python my_app.py

/usr/user1/python3.9-ENV2/

à remove /usr/user1/python3.9-ENV1/ from path


à add /usr/user1/python3.9-ENV2/ to (front of) PATH
à python my_other_app.py

à works but can become tedious as well!

23
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Virtual Environments
à these are used to perform the exact same steps
à make copy of Python installation
à provides scripts to "activate"/"deactivate" the environment
à unsets old path / sets new path

à very efficient on Unix/Mac – uses symlinks

à little less efficient on Windows – actually copies the files

à provides solution to version conflicts


à use them!!

24
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Creating Virtual Environments
à different implementations of this have evolved over the years

à Python now has a virtual environment manager built-in

à we'll use that one in this course

à first decide which Python version to use


à Mac: python3.8, python3.9, etc

à Windows: specify full path to python version

<python version/path> –m venv <your_env_name>

à creates a new virtual environment

25
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Activating the Virtual Environment

à activating a virtual environment essentially modifies your PATH

à when you type python on command line after activating environment

à you will be running version of Python located in that


environment directory

à different for Mac/Linux vs Windows

Mac/Linux: source <path_to_env>/bin/activate

Windows: <path_to_env>\Scripts\activate.bat

26
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

27
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Installing Packages

28
FooterLicensed to Kevin Romanteau. Email address: [email protected]
pip: Package Installer for Python

à installing 3rd party libraries (packages) basically requires copying files


into the Python installation (whichever directory you want)
à can be done manually, but again, tedious

à instead can use another app that is installed alongside Python


à pip
à easily install, update and remove packages

à uses the Python Package Index https://pypi.org

à official 3rd party repository for Python


à a repository of over 200,000 Python packages!

29
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Installing Packages with pip
à activate the virtual environment first (sets your PATH)
à pip install package_name
à can even specify versions
pip install package_name==1.3.2

pip install package_name<=1.2

pip install package_name>2.0

à once you have decided on a specific version of a package for your


project you should always use same version number if creating a new
environment for the same project
à otherwise you could end up breaking your own code!

30
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The requirements.txt File

à use a file, alongside your code, to keep track of required packages and versions
requirements.txt
numpy==1.18.1 à file name can be anything
pandas==1.1.4 à requirements.txt is a standard
matplotlib==3.3.3 convention

> pip install –r requirements.txt

à easier (install everything with one command)


à reproducibility / consistency
à documentation

31
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à for this course a requirements.txt file is available in your downloads

à defines (and pins) all specific libraries we'll need

à please use it so we're all using the same library versions


(functionality can change from version to version)

à will install many libraries (and their dependencies)


requests
pytz
numpy
pandas and more…

32
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Summary of Steps

à create a new virtual environment


à activate the environment
à pip install libraries (aka packages)

à don't forget to activate the environment before you pip install!


à otherwise pip install will install these packages to your
reference Python install

33
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

34
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Running Python

35
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Python Compiler/Interpreter

Intermediate
Python Python
Code
code compiler
(bytecode)

Virtual
Machine
à Python is both a compiler (OS/Platform
specific)
and an interpreter

Operating
System

36
FooterLicensed to Kevin Romanteau. Email address: [email protected]
How do we "run" Python?
à Python is a compiler/interpreter
à reads in a chunk of code (your program)
à compiles and runs it
à output is sent to your screen (console)
à Python can do this in two ways
à interactive mode
à you type a Python line/block of code and execute it immediately
à any output is immediately displayed
à continue typing/running code one line/block at a time
à REPL (read-eval-print-loop)
à script mode
à write all your code in files first
à then execute all this code using command line
37
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Interactive Mode
à activate virtual environment
à start Python shell (REPL) by typing python on command line
à start typing Python commands
à non graphical interface
à perfect when working on GUI-less servers
à little tedious to use when you are just trying things out

à Jupyter Notebooks
à browser based REPL (needs to be pip installed)
à much easier/nicer to use than command line
à can save your projects into a file (a notebook)
à usually .ipynb extension
38
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Script Mode
à write all your code using a text editor

à run your Python program using command line


> python my_app.py

à traditional programs are great for


à running the same program over and over again
à better structure
à complex applications (web server, prediction server, libraries, …)

39
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Python IDEs
IDE à integrated development environment
à a text editor
à with many extras for easily running code, debugging, and more
à runs code using the same command line approach for scripts

à many popular IDEs / editors around


à PyCharm
à VSCode this is the one I personally use for development

à Atom
à Sublime Text 3
à Spyder and more…
40
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

41
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Intro to Python for Financial Services

Python Basics

3 42
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à some basic Python types
à integers à floats à booleans

à a brief explanation of what objects are

à basic operators
à arithmetic operators
à integer division and modulus
à comparison operators
à Boolean operators
à operator precedence

43
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Basic Data Types

44
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Types
à Entities in a program always have an associated type
à in the real world too!
John is a person
My local pharmacy is a store
My bank balance is a (real) number
The number of pages in a book is an (integer) number
The file budget.xlsx is an Excel spreadsheet
Statements can be True or False
they have a type à Boolean type (True, False)
"I am a Python dev" is True
"My dog likes cats" is False
45
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Integers
à the int type
à used to represent integral numbers: 0, 1, 100, -100, etc.
à integers have an exact representation in Python
à integers can be of any magnitude
(as long as you have enough memory!)

à integer numbers can be created from a literal in the Python code


à 100
note how you can use underscores for
à -100 readability
à 10_500_000
à or, as the result of some calculation (expression)
à1 + 1
46
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Floats
à the float type

à used to represent real numbers (floating point): 3.14, -1.3

à can use Python literals to define a float


à 3.14
à -1.3
à 1_234.567_876

à the decimal point differentiates a float from an int when using literals
1 à int
1.0 à float

47
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Float Representations
Consider the decimal system: 1.234

In the decimal (base 10) system, this is representable (exactly) using powers of 10:
2 3 4
1+ + +
10 100 1000
1
But not all real numbers have a finite representation
3
as a fraction this is exact à but not using a decimal representation

1 3 3 3
̇
= 0.333 = + + +⋯
3 10 100 1000

à infinite number of fractions


48
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Integer Representations
à computers only "know" two numbers

à 0 and 1 à binary system, aka base 2

à any number in a computer is represented using powers of 2


the binary number 1011
can be converted to a decimal number:
1×2- + 1×2. + 0×2/ + 1×20
=1+2+0+8
= 11

49
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Float Binary Representation
in the same way, floats are represented using powers of 2 and fractions of
powers of 2
1 0 1 1 0 1
+ + = + +
2. 2/ 20 2 4 8
= 0.5 + 0 + 0.125 = 0.625
.
à we saw that certain numbers do not have a finite decimal representation ( )
0
à same happens with binary representations!
0 0 0 1 1 0 0 1 1
0.1 = + + + + + + + + + ⋯
2 4 8 16 32 64 128 256 512

0.09375

0.099609375
50
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Floats are not always exact
à bottom line: not all exact decimal numbers have an exact float representation

à not a limitation of Python


à all languages (incl. Excel) that use these binary fractions have that issue

⚠ be careful when comparing floats to one another

à there is a data type that can handle exact representations of decimal fractions

à Decimal (we will look at this type later)

⚠ calculations using Decimal are much slower than float

51
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

52
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Objects

53
FooterLicensed to Kevin Romanteau. Email address: [email protected]
What are objects?
à entities created by Python

à they have state (data)


encapsulation
à they have methods (functionality)

à they often represent real world things

Car
State (data) Functionality
• brand à Toyota
• accelerate()
• model à Prius LE
• brake()
• # doors à 4
• set_cruise_control()
• model_year à 2020
• left_turn_signal_on()
• odometer à 5_402

54
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Integers are Objects

à an int is an object

à it has state – the value of the integer

à but it also has functionality

à knows how to add itself to another integer


(10).__add__(100) à 110

à an integer object has the method __add__ used to implement addition


(this is not how we typically add two integers together – but that's just syntax)
à knows how to represent itself as a string (e.g. for visual output)

55
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Floats are Objects

à float numbers are objects too

state à value
functionality à __add__

other functionality too, for example:

(0.125).as_integer_ratio() à 1, 8

56
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Everything in Python is an object

à any data type we have in Python is actually an object

à it has state
attributes
à it has functionality

attributes encompass state and functionality

some attributes are for state


some attributes are for functionality

57
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Dot Notation
If an object has attributes, how do we access those attributes?
à dot notation

car.brand à accesses the brand attribute of the car object


car.model à accesses the model attribute of the car object

For attributes that represent functionality, we usually have to call the


attribute to perform the action
à often supplying additional parameters
car.accelerate(10, "mph")
(10).__add__(100)

58
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Mutability and Immutability
à an object is mutable if its internal state can be changed
à one or more data attributes can be changed

à an object is immutable if its internal state cannot be changed


à the state of the object is "set in stone"
In Python many data types are immutable: • integers
• floats
• booleans
• strings
• …
While some are mutable: • lists
• dictionaries
• sets à we'll cover all these in
• … this course
59
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

60
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Variables

61
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Naming Objects

We often need to label objects with some name

à reminds us what the object is used for

apy account_balance

à allows us to use the same object in multiple parts of our code

62
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Assigning Names
To assign a label to an object we use the assignment operator =
account_balance = 1000.0
this is not the
apy = 0.25 mathematical
equality
symbol
à we are assigning the label apy to the object 0.25

à we say apy is a reference to the float object 0.25

à the symbol apy is just a label currently pointing to (or referencing)


the object 0.25

63
FooterLicensed to Kevin Romanteau. Email address: [email protected]
References and Variables
Another way of looking at this: some object in memory

float
0.25
references
the object

apr

a label apr = 0.25


à apr is called a variable
à but it is just a label (a symbol) that references some object in memory
64
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Variables
So why the term variable?
à over time, which object a symbol references can change
a = 100 à a is referencing the object 100
later in the program…
a = True à a is now referencing the object True
à the state of the object the symbol references can change (mutate)

list
a 1, 2, 3 , 4

à a is still referencing the same we append an


object, but the object's state has element to the
changed (mutated) list
65
FooterLicensed to Kevin Romanteau. Email address: [email protected]
How Variable Assignment Happens

apy = 0.25
LHS RHS

à Python evaluates the RHS first

à then it "assigns" that result to the symbol in the LHS


(the LHS becomes a named reference to whatever results from the RHS)

Generally, RHS could be a more complex expression than just a literal

balance = 1000.0 – 50.0 à in both cases, the RHS


circ = 2 * 3.14 * 1.5 is fully evaluated first

66
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Using Variables
Once a variable has been created, it can be used elsewhere in the program

pi = 3.1415 pi 3.1415

radius = 1 radius 1

circ = 2 * pi * radius circ 6.283

àcirc is now a reference to the float 6.283

radius = 2 radius 2

BUT this does not change circ


à it still points to 6.283 circ 6.283

67
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Variable Naming

à case sensitive apr is a different symbol than APR

à must follow certain rules

à should follow certain conventions

68
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Must-Follow Rules

start with underscore (_) or letter (a-z A-Z)


(unicode characters are actually OK, but stick to a-z A-Z)

followed by any number of underscores or letters, or digits (0-9)

var my_var index1 index_1


all legal
_var __var __add__

à cannot be reserved words


True False if def and or
and many more we'll come across in this course

69
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Should-Follow Conventions

PEP 8 Style Guide à typical conventions followed by most Python devs

https://www.python.org/dev/peps/pep-0008/

terminology:
camel case àseparate words are distinguished by upper case letters
accountBalance BankAccount

snake case à separate words are distinguished by underscores

account_balance bank_account

70
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Should-Follow Conventions
For standard variables:
à snake case
à all lower case letters
account_balance ✅

account_Balance ❌

We'll see other conventions for other special types of objects throughout
this course

71
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Should-Follow Conventions

à Good idea to follow standard conventions

à but sometimes you may want to break those conventions

à that's OK – just have a good reason, and be consistent

From the PEP 8 Style Guide:

A foolish consistency is the hobgoblin of little minds.


(Emerson)

72
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

73
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Arithmetic Operators

74
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Terminology
An operator is a programming language symbol that performs some operation on
one or more values
Certain types of operators include:
à arithmetic operators
à comparison (or relational) operators
à logical operators

The values an operator acts on are called operands


à An operator that works on a single operand is called a unary operator
à An operator that works on two operands is called a binary operator
à An operator that works on three operands is called a ternary operator

75
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Arithmetic Operators
Unary Operators
- Unary Minus -10
+ Unary Plus +10

Binary Operators
+ Addition 10 + 20
- Subtraction 20 - 10
* Multiplication 10 * 2
/ Division 10 / 2
** Power (exponentiation) 2 ** 4

à use parentheses ( and ) to group expressions


76
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Operand Types
Arithmetic operators can act on any numerical type
int float
à as well as other types we'll encounter later
à what the operator does is actually determined by the type of the operands

à an operator may support mixed operand types

2 + 2 à returns an int
2 + 2.0 à returns a float
5.5 * 2 à returns a float
4 / 2 à also returns a float!

77
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The Power Operator
The power operator works just like its mathematical counterpart

2 ** 4
à 2 * 2 * 2 * 2
à 16 (int)

1
Recall from math: 245 =
25

2 ** (-4)
à 1 / (2 ** 4)
à 1 / 16
à 0.0625 (float)

78
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The Power Operator
à Python supports floats for either operand of the ** operator
à just like mathematical exponentiation

à graph of 𝑓 𝑥 = 𝑒 9

à 𝑏 9 : = 𝑒 9 <=>(@)

à Python also supports negative bases with real exponents


à complex numbers
à it's actually a numerical type in Python (complex)
79
FooterLicensed to Kevin Romanteau. Email address: [email protected]
How Python Implements Arithmetic Operators
à recall: numbers are actually objects
à they have state
à they also have functionality

à one of these is the __add__ method (amongst many others)

when we do this: a + b where a = 10 and b = 20

à 10 is an int object that implements the __add__ method

Python actually does this to evaluate the expression:

à a.__add__(b)
à this works the same way with other types

80
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Looking ahead…
à any type can choose to implement __add__ however it wants
à Python will then use that method to evaluate type_1 + type_2

à we will see later how to create our own types

à we can implement __add__ to define + for our custom type

à we'll look at this in code, though some of the code may not make sense (yet!)

81
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

82
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Operator Precedence

83
FooterLicensed to Kevin Romanteau. Email address: [email protected]
When we write an expression such as this: 2 * 10 + 5

à what does it mean?


(2 * 10) + 5 à 25 ? or 2 * (10 + 5) à 30 ?

Python chooses this

why?

à operator precedence

84
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Operators have precedence
à an operator with higher precedence will bind more tightly
à fancy way of saying it will be evaluated first

Precedence order with arithmetic operators

lower binary + - (equal precedence – since it does not actually matter)

* /

unary + -
higher
** except for a unary operator to the right of **

2 * 10 + 5
* has higher precedence than +
à 2 * 10 is evaluated first
à 20 + 5 à 25

85
FooterLicensed to Kevin Romanteau. Email address: [email protected]
** has highest precedence in our previous list

2 * 2 ** 3 à 2 * (2 ** 3) à 2 * 8 à 16

-2 ** 4 à -(2 ** 4) à -16
(as opposed to (-2) ** 4 à 16)

à except when unary operator is to the right of **

2 ** -3 à 2 ** (-3) à 0.125
à makes sense, difficult to interpret it otherwise anyway

86
FooterLicensed to Kevin Romanteau. Email address: [email protected]
A complete list of all operator precedence in Python can be found here:

https://docs.python.org/3/reference/expressions.html#operator-precedence

à my advice

à relying on operator precedence is tricky


à very easy to introduce bugs

à use parentheses

à it's just a few keystrokes more and will save a lot of pain later!

use (2 * 10) + 5 instead of 2 * 10 + 5

87
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

88
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Integer Division and Mod

89
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Let's review long division!

43
2 is the remainder
3 131
12
11 /
131 / 3 à 43
9 0

43 is the integer portion of the division

Python integer division: // 131 // 3 à 43

Remainder: use Python mod operator % 131 % 3 à 2

90
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The // Operator
a // b calculates the "integer portion" of a / b
à easy to understand when a and b are positive

Reality: a // b is the floor of a / b


floor(x) à the largest integer number <= x

-3.14 3.14
-4 -3 3 4

floor(-3.14) à -4 floor(3.14) à 3

12 / 5 à 2.4 12 // 5 à 2
-12 / 5 à -2.4 -12 // 5 à -3

91
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The mod Operator
Again negative numbers complicates things a bit!
à I said you can use % to calculate the remainder of dividing a by b
à in this case, for positive integers, a and b
à a % b and the remainder of dividing a by b is the same
à intuitive for positive numbers

But % is defined for negative integers and even floats as well

à what does that even mean?

92
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The mod Operator
/
Let's go back to our first example 131 / 3 à 43
0

131 131 131 𝑚𝑜𝑑 3


= 𝑓𝑙𝑜𝑜𝑟 +
3 3 3

a / b = a // b + (a % b)/b

à a % b = b (a / b – a // b)

à a % b = a - b (a // b)

So a % b is defined as the value that satisfies the above equation


à and that's how a % b is well-defined for negative values
à and even for floats!
93
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à this explains the "weird" (aka non-intuitive) behavior for negative numbers

12 % 5 à 2 12 % -5 à -3 -12 % 5 à 3 -12 % -5 à -2

à as well as how it works for real numbers 12.5 % 3 à 0.5


12.5 // 3 à 4.0
a % b = a - b (a // b)

à 12.5 % 3 = 12.5 – 3 (4.0) = 12.5 – 12.0 = 0.5

à moral: be careful using "intuition" for % and // and negative values

à fortunately most of the problems we work with involve positive integers


(more in coding video)
94
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

95
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Comparison Operators

96
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à also know as relational operators

à compares two things and yields a Boolean (bool) result


== equality comparison à != for "not equal"
<, <=, >, >= assumes the operands are comparable

10.5 < 100 à makes sense

hello > 100 à doesn't really make sense

à == between operands that are not comparable usually returns False

à <, <=, etc between non-comparable operands usually generates an Exception


à TypeError (we'll come back to exceptions later)

97
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à int and float types are comparable to each other
10 <= 10.9 à True

Equality between integers is straightforward

5 == 5 à True 5 == 6 à False

floats are a different story!


0.1 + 0.1 + 0.1 == 0.3 à False

à in general: never use == to compare floats

98
FooterLicensed to Kevin Romanteau. Email address: [email protected]
What does it mean for two objects to be equal?

à everything in Python is an object


1 is an int object 1.0 is a float object

are 1 and 1.0 the same object? à No!


à but they are the same value

à need to differentiate what equality means


à the object itself

à the "value" (or state) of the object

99
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Identity vs Value Equality of Objects

To see if two objects are the same object à is

To see if two (compatible) objects are equal in value (in some sense) à ==
à in most cases use ==
à we'll see situations where using is makes more sense

a = 1 a == b à True
b = 1.0 a is b à False
c = 1
c is c à True
d = 500 d == e à True
e = 500 but… d is e à False à d and e are not
the same objects!
100
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Identity vs Value Equality of Objects

The is operator is purely concerned with the memory address (identity) of


the objects
à is is called the identity comparison operator
The == operator, is, like +, actually implemented by the type itself

à recall: a + b actually executes a.__add__(b)

à == works the same way, using the __eq__ method

a == b à a.__eq__(b)

So we can define what == means for custom types, by implementing __eq__


(we'll see this later in this course)
101
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Other Comparison Operators

à other comparison operators we'll cover in this course

à membership operators: in and not in

à works with collection types


à determines membership in some collection

s = {1, 2, 3.14, True, 5.1} (like a mathematical set)


1 in s à True
10 in s à False
10 not in s à True
102
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

103
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Boolean Operators

104
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à in Boolean algebra we only have two values: True and False

à and three basic operators: and, or, not

à Python syntax:

not is a unary operator not True


not (a < b)

and, or are binary operators True or False


True and False
(enabled == True) and (withdrawal <= balance)

105
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The not Operator
à not simply reverses the Boolean value

Truth Table

a not a
True False
False True

106
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The and Operator

à a and b is True if and only if both a and b are True


à False otherwise

a b a and b
True True True
True False False
False True False
False False False

notice something interesting:


à if a is False, then a and b is always False, no matter what b is

107
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The or Operator

à a or b is False if and only if both a and b are False


à True otherwise

a b a or b
True True True
True False True
False True True
False False False

notice something interesting:


à if a is True, then a or b is always True, no matter what b is

108
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Short-Circuited Evaluation
à left and right operands are not restricted to values
à can be expressions too e.g. sin(a) > 0 and cos(a) < 0
given a value a calculate sin(a) à evaluate sin(a) > 0
à result_1
calculate cos(a) à evaluate cos(a) < 0
à result_2
evaluate result_1 and result_2
à 4 calculations plus the and operation
à but what if result_1 had been False (i.e. sin(a) was not positive)?
à recall: if a is False, then a and b is always False, no matter what b is
à irrespective of what cos(a) < 0 evaluates to, the result will always be False
à so if the left operand evaluates to False, we don't even to
calculate the right operand to get an answer à short-circuited evaluation
109
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Short-Circuited Evaluation
The same happens with a or b
if a is True, then result is True, irrespective of what b is
à Python returns True without evaluating b

And as we just saw with a and b


if a is False, then result is False, irrespective of what b is
à Python returns False without evaluating b

à short-circuited evaluation
à can be very useful
à will see examples of this in section on conditional execution
110
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Example of Short-Circuiting Usefulness
à suppose we have some trading algorithm that can calculate some buy
signal (True/False)
à the catch is that the calculation is complex and resource intensive
à in addition, we only want to place an order if the exchange is open
we could write some code to do this:
if calc_signal(symbol) and exchange_open(symbol):
buy(symbol)

à problem: when exchange is closed we needlessly calculate the signal


à but because of short-circuiting we can write:
if exchange_open(symbol) and calc_signal(symbol):
buy(symbol)
à this way if exchange is closed, we don't even calculate the signal
111
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

112
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Intro to Python for Financial Services

Conditional Execution

4 113
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à one of the fundamental constructs in programming is conditional execution

à if something is true
à run some code

à else (optionally)

à run some other piece of code

114
FooterLicensed to Kevin Romanteau. Email address: [email protected]
For example, for an ATM withdrawal:

à if amount does not exceed available funds and does not exceed daily limit
à dispense cash
à print receipt
à otherwise
à deny request
à display some text on screen
à print slip containing reason

à this is the primary reason we studied conditional expressions in the last chapter!

115
FooterLicensed to Kevin Romanteau. Email address: [email protected]
if… else…

116
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The if Statement
note the colon!
if <expression evaluates to True>:
code line 1
code line 2 notice how this code block is indented

à this tells Python that all these lines should
be executed if the condition is True

à you "exit" a code block by unindenting your code

à Python uses code indentation to group together chunks of code


à called code blocks

à if you are familiar with other languages such as Java or C/C++, this is
equivalent to using braces {} 117

FooterLicensed to Kevin Romanteau. Email address: [email protected]


Examples
price = 200
if price < 250:
make_purchase()

à the call to make_purchase() will only be executed if price < 250


evaluates to True, which in this case it is

price = 300
if price < 250:
make_purchase()

à in this case make_purchase() is not executed

118
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Beware!
à unindenting code from a block, "exits" the block
à the following is a common mistake

price = 150
if price < 100:
this is the
print('price is below 100, buying…')
code block
make_purchase()

à price < 100 is False


à does not run code in the if block
(if block only contains a single statement – the print statement)

à runs make_purchase() à bug!!

119
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The else Clause
à often in conditional execution
if something is True
à do something
otherwise
à do something else

à Python's if statement supports an else clause à it is optional

if <expression is True>: note how else is unindented


[Code Block 1] from the if block, and
else: followed by a colon
[Code Block 2]
indent to form the else block
120
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Example
price = 200

if price < 250:


print('The price is right!')
else:
print('Too pricey!') notice this line of code is unindented
print('Done.') à it has nothing to do with the
if or else blocks
à it will always execute
à price < 250 is True
à the if block is executed The price is right
à the else block is skipped
à code resumes after the else block Done
121
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Example
price = 300

if price < 250:


print('The price is right!')
else:
print('Too pricey!')
print('Done.')

à price < 250 is False


à the if block is skipped
à the else block is executed Too pricey!
à code resumes after the else block Done.

122
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Nested if Statements

à sometimes we need to nest conditional logic, either in the if block or


in the else block

if price < 1000: à if price < 1000 is True


if price < 500: à if price < 500
volume = 50 à set volume to 50
else: à otherwise
volume = 10 à set volume to 10
make_purchase(volume) à purchase specified volume
else: à otherwise
print('Too pricey!') à Too pricey

123
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Nested if Statements
à the nesting can occur in the else block too

if price < 1000:


make_purchase()
else:
if price < 2000:
contact_vendor()
else:
find_new_vendor()

à can nest to any number of levels


à too much nesting can make code hard to read!
à keep it to a minimum

124
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

125
FooterLicensed to Kevin Romanteau. Email address: [email protected]
elif

126
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Multi-Level if Statements
Consider this example to calculate a grade letter given a numeric grade:

if grade >= 90:


grade_letter = 'A'
else:
if grade >= 80:
grade_letter = 'B' à that's a lot of nesting!
else:
if grade >= 70: à hard to read (for humans)
grade_letter = 'C'
else:
if grade >= 60:
grade_letter = 'D'
else:
grade_letter = 'F'
127
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The elif Clause

Instead of this nested structure, Python provides an elif clause


à equivalent to a nested else-if
à does not require this double indentation
à easier to read!
once an if or elif
clause executes (is True)
if grade >= 90:
grade_letter = 'A' à no other if, elif or
elif grade >= 80: else block executes
grade_letter = 'B'
else:
grade_letter = 'F'
else executes if no if or
elif statement executed
128
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Grade Letter Example
if grade >= 90:
grade_letter = 'A' if grade >= 90:
else: grade_letter = 'A'
if grade >= 80: elif grade >= 80:
grade_letter = 'B'
grade_letter = 'B'
else:
if grade >= 70: elif grade >= 70:
grade_letter = 'C' grade_letter = 'C'
else: elif grade >= 60:
if grade >= 60:
grade_letter = 'D'
grade_letter = 'D'
else: else:
grade_letter = 'F' grade_letter = 'F'

à much more human readable!

129
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

130
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Ternary Conditional Operator

131
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Terminology

unary operator à an operator that takes a single operand


à operator usually a prefix to the operand
à -x

binary operator à an operator that takes two operands


à usually operands are on either side of the operator
àx + y

ternary operator à an operator that takes three operands

à so how do we write that? 🤔


132
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à suppose we have an operator that takes three operands: a, b, c

à the goal is for the operator to return a + (b * c)


à this is a thing – it's called the Multiply-Accumulate operator (MAC)
(but not available in Python!)

à maybe this? a accmul b, c

à or maybe this? a acc b mul c

all we've done here is split the name of the operator into two
and added the operands in between

133
FooterLicensed to Kevin Romanteau. Email address: [email protected]
This type of conditional code is often used
if <conditional exp>:
var = value1
else:
var = value2

à key is that each code block is a single assignment


à to the same variable

à Python introduces a conditional ternary operator to do this

134
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The conditional ternary operator
à remember that an operator operates on operands and returns
(calculates) some result

if <conditional exp>:
var = value1
else:
var = value2

à in this case we want the ternary operator's operands to be:


à the conditional expression
à the value to return if the expression is True
à the value to return if the expression is False

135
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The conditional ternary operator

if <conditional exp>:
var = value1
else:
var = value2

value1 if <conditional exp> else value2

à this is a single ternary operator


à if condition is True, it returns value1
à if condition is False, it returns value2

136
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Example

if price < 100:


volume = 10
else:
volume = 1

à can be re-written using a conditional ternary operator

volume = 10 if price < 100 else 1

137
FooterLicensed to Kevin Romanteau. Email address: [email protected]
General Form

à we saw examples where we used values as the return operands


à but it's more general than that

à the two value operands can be any expression


à the result of the expression is then used

<exp1> if <condition> else <exp2>

var = (a – b) if a > b else (b – a)

138
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Short-Circuiting

Just like we saw with Boolean operators, the ternary operator also uses
short-circuit evaluation

<exp1> if <condition> else <exp2>

à first evaluates <condition>


à if it is True, evaluates and returns <exp1>
à but does not evaluate <exp2>
à if it is False, evaluates and returns <exp2>
à but does not evaluate <exp1>

139
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Example
result = a / b if b != 0 else 'NaN'

a = 10 à returns 2
b = 5 à b is 5, so b != 0 evaluates to True
à a / b is calculated and returned

a = 10 à this works just fine, and returns NaN


b = 0 à b is zero, so b != 0 evaluates to False
à a / b is not calculated
(thereby avoiding a division by zero exception)
à NaN is returned

140
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

141
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Intro to Python for Financial Services

Sequence Types

5 142
FooterLicensed to Kevin Romanteau. Email address: [email protected]
What are Sequences?
à sequences are ordered collections of objects
à there is a first element
à there is a second element
à and a next one à sometimes called the sequential order

à we can index those elements using integers


à like counting them one by one

à but in Python (and most other programming languages)


à numbering starts at 0

à like anything in Python, sequences are objects


à they just happen to be container type objects that contain other objects

143
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Indexing Sequences
n objects à length of sequence

object, object, object, …, object


0 1 2 n-1

à n objects in sequence
à last element index is n-1

in this course:
à first element refers to the element at index 0
à second element refers to the element as index 1
à last element refers to the element at index n-1 (assuming n elements in sequence)

144
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Sequence Length

à sequences are usually finite

à but not all sequence types are

à in this course we'll stick to finite sequences

à first element
à last element
à finite length

145
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Homogeneous vs Heterogeneous Sequences

certain sequence types can only contain objects that are all the same type
à homogeneous sequence types

other types of sequences may contain objects that are of different type

à heterogeneous sequence types

146
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Sequence Types in this Chapter

lists à mutable heterogeneous sequence type

tuples à immutable heterogeneous sequence type

strings à immutable homogeneous sequence type

147
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Lists

148
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The list Type
à it is a container type à it contains elements

à it is a sequence type à elements are ordered sequentially


à elements are indexed
à lists can be heterogeneous

à lists are mutable à can add, replace or remove elements

à lists have unbounded growth à can add as many elements as we want


à but they are still finite
à lists are objects
à they have state à the elements contained in the list
à they have functionality à add element, remove element, etc

149
FooterLicensed to Kevin Romanteau. Email address: [email protected]
list Literals
à Python lists can be created using literals
[10, 20, 30, 40]

note the enclosing square brackets []


à this is what indicates the type is a list

à this list is homogeneous à all elements are integers

à but they don't have to be [10, 3.14, True]

à they can even be nested [10, 20, 30, [True, False]]

the last element of this list is itself a list


150
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Accessing list Items by Index
à lists are sequence types à sequential order à indexable

l = [10, 20, 30, 40, 50] (length is 5)


index 0 1 2 3 4

à we can reference an element by its index l[i]


l[0] à 10
l[1] à 20
l[2] à 30

Trying to access a list by index greater than last index will cause an exception!

l[5] à IndexError

151
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Sequence Length

l = [10, 20, 30, 40, 50]

à visual inspection à length of l is 5

à but we can use code to calculate this for us

à the len function

len(l) à 5

len([True, False]) à 2

152
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Empty Lists
sometimes we want to start with an empty list
and have code that adds to the list as our program runs

à to create an empty list we can just use a literal

l = []

then len(l) à 0

⚠ l[0] à IndexError

153
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Replacing a list Element
l = [10, 20, 30, 40, 50]
à we can retrieve elements by index
print(l[2]) à 30
à but we can also replace an element at index i with a different element
à we use the assignment operator =
l[2] = True
l = [10, 20, True, 40, 50]
print(l[2]) à True

we are replacing elements – so the index must be valid!


⚠ l[5] = 100 à IndexError
154
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

155
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Tuples

156
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The tuple Type
à very similar to the list type

à it is a container type

à it is a sequence type

à tuples can be heterogeneous

à BUT… they are an immutable container type

à unlike lists, once a tuple has been created

à cannot add or remove elements


à cannot replace elements

157
FooterLicensed to Kevin Romanteau. Email address: [email protected]
tuple Literals
à Python tuples can be created using literals
(10, 20, 30, 40)
note the enclosing round brackets ()
à this indicates the collection is a tuple

à just like lists, they can can contain any object, including another tuple

(10, 20, (3, 4))

(10, 20, (True, False), [100, 200])

158
FooterLicensed to Kevin Romanteau. Email address: [email protected]
tuple Literals
à often we don't even need the ()
à Python interprets a comma separated list of elements as a tuple
à so we can write (10, 20, 30)
à or just 10, 20, 30

à both these code snippets result in t being a tuple

t = (10, 20, 30)


t = 10, 20, 30

159
FooterLicensed to Kevin Romanteau. Email address: [email protected]
tuple Literals
à just like lists, tuples can contain any object
à including other tuples or lists

(1, [True, False], (3, 4))

list tuple

tuple

à we can omit the parentheses on the outer tuple


1, [True, False], (3, 4)

à but not (3, 4)


1, [True, False], 3, 4 à not the same
160
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Indexing, Length
à just like lists, elements can be read back from a tuple using an index number

à the len() function works with tuples also

t = 10, 20, 30, 40, 50

len(t) à 5

t[0] à 10

t[2] à 30

t[5] à IndexError

161
FooterLicensed to Kevin Romanteau. Email address: [email protected]
tuples are Immutable

à unlike lists, we cannot replace an element of a tuple


t = 10, 20, 30

t[0] = 100 à TypeError

à the container is immutable


à does not mean elements in the container are immutable
last element is a list
t = 10, 20, [True, True]
à which is mutable
t[2] = 100 à TypeError

t[2][1] = False t à 10, 20, [True, False]

162
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Creating Empty tuples

à not very useful, so not used very often

à use empty parentheses

t = ()

à that tuple is immutable, so it will remain empty for its lifetime

163
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

164
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Strings

165
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The str Type

à this is also a container type

à it is a sequence type

à strings are homogeneous à they can only contain characters (unicode)

à they are immutable

166
FooterLicensed to Kevin Romanteau. Email address: [email protected]
str Literals

à Python strings can be created using literals

'this is a string'
note the enclosing quotes '…'
à can also use double quotes
"this is a string"

note the enclosing double quotes "…"

à these quotes/double-quotes are called the string delimiters

à an empty string literal can be '' or ""

167
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Indexing, Length

à works the same way as any sequence type


à use an index number to access elements of the string
à use the len() function to find the length of the string

s = 'Python'

len(s) à 6

s[0] à 'P' (a string containing a single character)


s[1] à 'y'
s[5] à 'n'
s[6] à IndexError

168
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

169
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Slicing

170
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à slicing is a way to extract ranges of elements from a sequence

à start position (by index number)


à stop position (by index number)

[start:stop]

à start index is inclusive of the element


à stop index is exclusive of the element
à slices are the same type as the sequence being sliced

171
FooterLicensed to Kevin Romanteau. Email address: [email protected]
l = [10, 20, 30, 40]
0 1 2 3

l[0] à 10 l[1] à 20 l[2] à 30 l[3] à 40

l[0:2] à starts at 0, and includes element at 0


à ends at 2, but excludes element at 2
l[0:2] à [10, 20] à result is also a (new) list
l[1:3] à [20, 30]

t = (10, 20, 30, 40)


0 1 2 3

t[0:2] à (10, 20) à result is also a (new) tuple


t[1:3] à (20, 30)
172
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à str type is a sequence type à slicing for strings works the same way

s = 'Isaac Newton'
0 1 2 3 4 5 6 7 8 9 10 11

s[0:4] à 'Isaa'

s[0:5] à 'Isaac'

s[6:9] à 'New'

173
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Including Last Element in Slice
s = 'Isaac Newton' s[6:11] à 'Newto'
0 1 2 3 4 5 6 7 8 9 10 11

à how do we specify including the last element ?


à it's ok to specify indexes outside the sequence bounds!
à Python will automatically figure it out

s[6:12] à 'Newton'

s[6:1000] à 'Newton'

à we can also leave the end index blank


à Python will interpret as "up to and including the last element"
s[6:] à 'Newton'

174
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Including First Element in a Slice
s = 'Isaac Newton'
0 1 2 3 4 5 6 7 8 9 10 11

à just specify 0 as the start s[0:5] à 'Isaac'

à can also leave the start index blank

s[:5] à 'Isaac'

àthis is actually valid: s[:] à 'Isaac Newton'

à this made a shallow copy of the sequence

à we'll come back to that in a bit

175
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Slicing with Steps
à a step is a way to specify an interval when slicing a sequence

s[start:stop:step]

[2:10:2] à start at (and include) index 2


à end at (but exclude) index 10
à move in steps of 2

2 3 4 5 6 7 8 9 à indexes: 2 4 6 8

l = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
0 1 2 3 4 5 6 7 8 9

l[2:10:2] à [30 ,50 ,70 ,90]

176
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Negative Steps
à possible to use negative step values

à starts at index start (inclusive)


à stops at index end (exclusive)
à moves backwards à so start should be greater than end

l = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
0 1 2 3 4 5 6 7 8 9

l[9:6:-1] à [100, 90, 80]


l[:6:-1] à [100, 90, 80]
l[3::-1] à [40, 30, 20, 10]
l[::-1] à [100, 80, 80, 70, 60, 50, 40, 30, 20, 10]
177
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à strings are sequence types à also works for strings

s = 'Isaac Newton'
0 1 2 3 4 5 6 7 8 9 10 11

s[11:5:-1] à 'notweN'

s[:5:-1] à 'notweN'

s[::-1] à 'notweN caasI'

s[10::-2] à 'owNcaI'

s[::-2] à 'nte as'

178
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

179
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Manipulating Sequences

180
FooterLicensed to Kevin Romanteau. Email address: [email protected]
àmutable sequences can be modified

à replace elements
à delete elements
à add elements
à often appended (to the end)
à can also specify where in the sequence to insert

181
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Replacing Single Elements

Replace an element at index i by assigning a new element to that index

l = [10, 20, 30]

l[1] = 'hello'

l à [10, 'hello', 30]

182
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Replacing an entire Slice

à can also replace an entire slice


à just assign a new collection to the slice
à slice will be replaced with elements in RHS

my_list = [1, 2, 3, 4, 5]

my_list [0:3] = ['a', 'b']


my_list [0:3] = ('a', 'b') my_list à ['a', 'b', 4, 5]
my_list [0:3] = 'ab'

à Python uses the elements of the sequence in RHS when assigning to a slice
(but not when assigning using a single index)
183
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Deleting Elements
à can delete an element by index
my_list = [1, 2, 3, 4, 5]

del my_list[1]
my_list à [1, 3, 4, 5]

à can delete an entire slice


my_list = [1, 2, 3, 4, 5]

del my_list[1:3]
my_list à [1, 4, 5]

184
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Appending Elements

à we can append one element


my_list = [1, 2, 3]
my_list.append(4)
my_list à [1, 2, 3, 4]

à to append multiple elements, we extend the sequence


my_list = [1, 2, 3]

my_list.extend(['a', 'b', 'c'])


my_list.extend(('a', 'b', 'c')) does the same thing
my_list.extend('abc')

my_list à [1, 2, 3, 'a', 'b', 'c']

185
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Inserting an Element
à instead of appending, we can insert at some index
à use sparingly – this is much slower than appending or extending

my_list = [2, 3, 4, 5]
my_list.insert(0, 100)
my_list à [100, 2, 3, 4, 5]

my_list = [2, 3, 4, 5]
my_list.insert(2, 100)
my_list à [2, 3, 100, 4, 5]

à element is inserted so its position is the index - remaining elements are shifted right
186
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

187
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Copying Sequences

188
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Shallow vs Deep Copies
à two types of copies
à shallow copies
à new sequence is created (not same sequence object as original)
à elements in new sequence reference the same elements as original

à deep copies
à new sequence is created (not same sequence object as original)

à each element in new sequence is a deep copy of the original


à totally new and independent objects

189
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Shallow Copy

obj 1
index 0 index 0
index 1 obj 2 index 1
index 2 index 2
obj 3

original shallow_copy

original is shallow_copy à False


à original and shallow_copy are not the same containers
à but the elements are referencing the same objects

190
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Shallow Copy
obj 1
index 0 index 0
index 1 obj 2 index 1
index 2 index 2
obj 3

original shallow_copy
à add/remove/replace element in one does not affect the other

obj 1 index 0
index 0
index 1 obj 2 index 1
index 2 index 2
obj 3 index 3

original obj 4
shallow_copy

191
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Shallow Copy
à but mutating an element will affect both (since it is a shared reference)

obj 1
index 0 index 0
index 1 obj 2 index 1
index 2 index 2
obj 3
original
(modified)
shallow_copy

192
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Creating Shallow Copies

à use slicing to slice the entire sequence à my_list[:]

à use the copy method à my_list.copy()

my_list = [1, 2, 3]
my_copy = my_list[:]
my_copy à [1, 2, 3]
my_copy = my_list.copy()

del my_copy[0] my_copy à [2, 3]


my_list à [1, 2, 3]

193
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Mutable Elements

my_list = [['a', 'b'], 2, 3]

my_copy = my_list.copy()

à my_list[0] and my_copy[0] are both referencing the same list ['a', 'b']

my_list[0] is my_copy[0] à True

so if we modify that element (from either sequence):


my_copy[0].append('c')
my_copy[0] à ['a', 'b', 'c']
my_list[0] à ['a', 'b', 'c']

194
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Creating Deep Copies

à uses deepcopy function in the copy module

from copy import deepcopy


my_list = [['a', 'b'], 2, 3]
my_copy = deepcopy(my_list)

my_list[0] is my_copy[0] à False à element has been copied too!

my_copy[0].append('c')
my_copy[0] à ['a', 'b', 'c']
my_list[0] à ['a', 'b']

195
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

196
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Unpacking Sequences

197
FooterLicensed to Kevin Romanteau. Email address: [email protected]
consider a sequence
data = (1, 2, 3) à this is a tuple with three elements

we want to assign those values 1, 2 and 3 to some symbols a, b and c resp.


à could do it this way: a = data[0]
b = data[1]
c = data[2]

but Python has a better way of doing this! unpacking

a, b, c = (1, 2, 3)

Since tuples don't actually need the parentheses in this case, we can write:

a, b, c = 1, 2, 3

198
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à this works with any sequence in general
a, b = [10, 20] a à 10
b à 20

a, b, c = 'XYZ' a à 'X'
b à 'Y'
c à 'Z'

199
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à beware!
à number of elements in sequence on RHS must match number of symbols on LHS

a, b = 1, 2, 3
à ValueError (too many values to unpack)

a, b, c = 1, 2
à ValueError (not enough values to unpack)

200
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Swapping Two Variable Values
à this is a common problem
given two variables a and b, swap the value of a and b
Initial State: a à 10 End State: a à 20
b à 20 b à 10

à typical solution uses a temporary variable

temp = a
a = b
b = temp

à 3 lines of code and an unnecessary variable

201
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Swapping Two Variable Values
à can use unpacking to our advantage
à remember: in an assignment, the RHS expression is evaluated completely first
à then the assignment takes places

a, b = b, a

à RHS is evaluated first b, a is the tuple 20, 10

à then the assignment is made a, b = 20, 10

à values of a and b have been swapped!

202
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

203
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Intro to Python for Financial Services

Strings

6 204
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à strings are sequence types

à but they are more specialized than generic sequences

à they are homogeneous


à each element is a single character

à we have additional functionality available

205
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Unicode

206
FooterLicensed to Kevin Romanteau. Email address: [email protected]
In the beginning…
… there was ASCII (American Standard Code for Information Interchange)
à addressed the problem of a standard for assigning

à numeric codes à to characters


à printable and non-printable

à and encoding the value into binary à using sequences of 7 bits


à given a data stream filled with 0's and 1's
à carve up in 7 bits and decode character

à fonts handle displaying the character


à a bunch of pixels
à a glyph
207
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à supported character set was limited
à 128 characters
à 95 printable characters (a-z, A-Z, 0-9, * / etc)
à 33 non-printable characters (control codes, e.g. esc, newline, tab, etc)

208
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à attempts were made to extend the ASCII set

à still far too limited


à standard was poorly followed

à Unicode was developed


à focused on assigning a code to a character (code point)
à does not specify how to encode the code points into a binary format
à other standards for doing that appeared
à UTF-8 ß very popular, default in Python
à UTF-16
à UTF-32 (utf à Unicode Transformation Format)
à > 100,000 code points defined so far

209
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Code Points
à backward compatible with ASCII
ASCII character code for A à 65 (decimal), 41 (hexadecimal)
Unicode code point for A à 65 (decimal), 41 (hexadecimal)

decimal à base 10 (0 – 9)
hexadecimal à base 16 (0-9, A-F)

210
FooterLicensed to Kevin Romanteau. Email address: [email protected]
What is hex anyway?
Decimal system – uses powers of 10 à 10 digits, 0-9
103 102 101 100
9034 = 4 ×10- + 3 ×10. + 0 ×10/ + 9 ×100
9 0 3 4

Binary – uses powers of 2 à 2 digits, 0-1

23 22 21 20
(1011)/ = 1×2- + 1×2. + 0×2/ + 1×20 = 11.-
1 0 1 1

Hexadecimal – uses powers of 16 à 16 digits, 0-9, A-F


163 162 161 160
A à 10, Bà 11, …, F à 15

F C 1 5 𝐹𝐶15 = 5×16- + 1×16. + 12×16/ + 15×160


= 64533.-
211
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Unicode Character A
https://www.compart.com/en/unicode/U+0041

the character (hex) code

the character name

corresponding lowercase letter

212
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à ord() function
à returns code point for a single character (in decimal)
ord('A') à 65

à hex()
à converts decimal to hex string
hex(65) à '0x41' (0x prefix indicates the number after that is in hex)

213
FooterLicensed to Kevin Romanteau. Email address: [email protected]
https://www.compart.com/en/unicode/U+03B1

hex(ord("α")) à '0x3b1'

copy/paste the glyph from that page


straight into your Python code

214
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Other ways to specify the character in a string
à use escape codes
à by hex code à by name

"\N{Greek Small Letter Alpha}" à "α"

"The letter \N{Greek Small Letter Alpha} is the first letter of the Greek alphabet."

à 'The letter α is the first letter of the Greek alphabet.'

"\u03b1" "\u03B1" \u must be followed by exactly 4 hex digits (0-F)

"The letter \u03B1 is the first letter of the Greek alphabet."

à'The letter α is the first letter of the Greek alphabet.'

215
FooterLicensed to Kevin Romanteau. Email address: [email protected]
https://www.compart.com/en/unicode/U+1F40D

"\N{snake}" à ' 🐍'

ànote how character code 1F40D has 5 digits


à must use \U followed by exactly 8 digits (\u is limited to 4 digits)

"\U0001F40D" à ' 🐍'


pad with zeroes to make 8 digits
216
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

217
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Common String Methods

218
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à Python has a ton of string methods
https://docs.python.org/3/library/stdtypes.html#string-methods

In this video we are going to look at some in these categories


à case conversions
à stripping start and end characters
à concatenating strings
à splitting and joining strings
à finding substrings

à methods, called using dot notation my_string.method()

à remember that strings are immutable


à operations never modify a string à just return a new string
219
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Case Mappings

lower() 'Hello World'.lower() à 'hello world'

upper() 'python'.upper() à 'PYTHON'

title() 'one two three'.title() à 'One Two Three'

à returns a new string

à primarily used for visual display

à BEWARE: may not work for caseless comparisons

220
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Case Folding

casefold() à used for caseless comparisons

s1 = 'hello'
s2 = 'HeLlo'

s1.casefold() == s2.casefold() à True

à we'll explore this vs using case mappings in the code section

221
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Stripping
sometimes we want to remove leading and trailing characters
à trailing commas
à whitespace around a string

.lstrip() à strips all whitespace on left of string


.rstrip() à strips all whitespace on right of string
.strip() à strips all whitespace on both ends of string

à can specify what characters to strip


.strip(' ') à strip space characters from both ends
.lstrip('abc') à strip the characters 'a', 'b', 'c' from left end
à returns a new string
222
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Concatenation
combining two or more strings to form a single string is called concatenation

'Hello' + ' ' + 'World!' à 'Hello World!'

à again, this creates a new string

223
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Splitting Strings
à useful for parsing data from a text file
data = '100, 200, 300, 400' ß a string containing comma
delimited values
à can easily split this on the comma

data.split(',')

à returns a list of strings ['100', ' 200', ' 300', ' 400']

note the spaces


à we can strip them later

224
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Joining Strings
à this is the opposite of splitting strings
suppose we want to join these strings with `, ` characters between each:
'a' 'b' 'c' 'd'

we could write:
'a' + ', ' + 'b' + ', ' + 'c' + ', ' + 'd'

à tedious to type out

à "hardcoded"

à what if we have a sequence of strings we want to concatenate


à this approach is not general enough

225
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Joining Strings

data = ('ab', 'cd', 'ef') à data is a sequence of strings

'--'.join(data) à join the strings in data with -- in between


à 'ab--cd--ef'

','.join(['10', '20', '30']) à '10,20,30'

à remember that a string is a sequence of single characters

'='.join('python') à 'p=y=t=h=o=n'

226
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Finding Substrings

à often just want to know if a sequence of characters is contained inside another

à use the in operator

'x' in 'xyz' à True


'a' in 'xyz' à False
'pyt' in 'python' à True
'pyt' in 'Python' à False

à tests containment
à but gives no indication of where the substring is

227
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à slight variation

à does the string start (or end) with the specified characters
à still a containment test

.startswith('…') .endswith('…')

'python'.startswith('py') à True

'python'.startswith('hon') à False

'python'.endswith('py') à False

'python'.endswith('hon') à True

228
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Finding the Index of a Substring
à used when we need to know the index of the start position of a substring

data = 'This is a grammatically correct sentence.'

à at what index does the string 'correct' occur?

data.index('correct') à 24

à what if substring is not found?

à Python raises a ValueError

à potentially useful once we learn how to handle exceptions

229
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Finding the Index of a Substring
à what if we don't want an exception?
à find à returns -1 if substring is not found

data = 'This is a grammatically correct sentence.'

data.find('correct') à 24

data.find('DOW') à -1

à once we know how to handle exceptions, this method is a bit redundant

à personally, I prefer using index and using exception handling

230
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Important Note

à only interested in whether or not a substring is contained in another string

à use in

à only use index or find when you need to know the index

à in is much faster!

231
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

232
FooterLicensed to Kevin Romanteau. Email address: [email protected]
String Interpolation

233
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à often we want to build strings that contain values from some variable

à can use concatenation

à + works with two strings


à cannot mix string and numeric for example
'test' + 100 à TypeError

'test' + str(100) à 'test100'

234
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Example

Suppose we have four variables: open_ = 98


high = 100
low = 95
close=99

We want to build a string that looks like this for display purposes:

'open: 98, high: 100, low: 95, close: 99'

à using concatenation:
'open: ' + str(open_) + ', high: ' + str(high) + ', low: ' + str(low) + ', close:' + str(close)

à tedious and error prone! à in fact there is an error, can you spot it?!
235
FooterLicensed to Kevin Romanteau. Email address: [email protected]
String Interpolation
à multiple variants à two most common techniques

à the format method


à use {} as placeholders in our string
à pass variables to format method in same order as we want them in the string
à number of {} in string and arguments in format should match
à format can have more arguments, they'll just be ignored
à IndexError exception if not enough arguments

'open: {}, high: {}, low: {}, close: {}'.format(open_, high, low, close)

à note how we did not have to convert the values to strings!


236
FooterLicensed to Kevin Romanteau. Email address: [email protected]
f Strings

à new to Python 3.6


à prefix the string with f
à use {expr} directly inside the string
à Python evaluates expr and interpolates the result directly inside the string

f'1 + 1 = {1 + 1}' à '1 + 1 = 2'

value = 3.14
f'pi is approximately {value}' à 'pi is approximately 3.14'

f'open: {open_}, high: {high}, low: {low}, close: {close}'

à 'open: 98, high: 100, low: 95, close:99'


237
FooterLicensed to Kevin Romanteau. Email address: [email protected]
f Strings
à could use do this as well
open_ = 98
high = 100
low = 95
close=99

f'open: {open_}, close: {close}, delta:{close – open_}'

à 'open: 98, close: 99, delta: 1'

238
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

239
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Intro to Python for Financial Services

Iteration

7 240
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à fundamental aspect of writing programs is repetition
à want to repeat the same process (code) multiple times

à how many times?


à known in advance
à load file with 10,000 rows
à process each row à repeat the same process 10,000 times
à deterministic

à not known in advance


à get commodity tick data
à analyze data until ask price falls below some level
à then do something else and stop processing
à process may repeat 10 times, or 100 times, we don't know in advance
à non deterministic
241
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à this repetition is called iteration

deterministic iteration
à we iterate over the elements of some container
à e.g. sequences
à more generally over objects that are iterable
à not all iterables are sequences
àa bag of marbles is iterable, but it is not a sequence!
à for loop

non-deterministic iteration
à we iterate while some condition is True
à while loop
242
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The range Function

243
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The range Object

à range object is an iterable object


à it serves up integers one by one as they are requested
à but the full list of integers does not exist all at once in memory
à memory efficient
à it has a finite number of integers

à we can iterate over that range object


à since it exists and has a finite number of integers à deterministic iteration

à we can use the range() function to create range objects

244
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The range() Function

à three flavors depending on how many arguments are specified

range(end) (one argument)


à generates integers from 0 (inclusive) to end (exclusive)

range(start, end) (two arguments)


à generates integers from start (inclusive) to end (exclusive)

range(start, end, step) (three arguments)


à generates integers from start (inclusive) to end (exclusive)
à in steps of step

à should remind you of slicing


245
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Viewing Contents of range Object
r = range(5)
print(r) à 'range(5)'
à not what we wanted

à can convert range object to a list or tuple

print(tuple(r)) à (0, 1, 2, 3, 4)

print(list(r)) à [0, 1, 2, 3, 4]

246
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Iteration

à range object is iterable

à we can use a for loop to iterate over the elements of this iterable

(next lecture)

247
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

248
FooterLicensed to Kevin Romanteau. Email address: [email protected]
for Loops

249
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à for loops are used to iterate over elements of any iterable

à the loop mechanism retrieves elements from the iterable one at a time
à the body of the for loop is executed for each element retrieved
à the loop terminates when all elements have been iterated

250
FooterLicensed to Kevin Romanteau. Email address: [email protected]
for x in ['a', 'b']:
y = x + x
note how the body is indented
print(y)
print('done') à just like if…else… code blocks
unindented à not in loop body
1st iteration:
'a' is retrieved and assigned to the symbol x
y is the concatenation of x and x à 'aa'
'aa' is printed to the console
2nd iteration:
'b' is retrieved and assigned to the symbol x
y is the concatenation of x and x à 'bb'
'bb' is printed to the console
3rd iteration: à no more elements à loop terminates
à code after loop executes
à 'done'
251
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Iterating over range Objects
à range objects are iterable

for i in range(4): range(4) à 0, 1, 2, 3


sq = i * i
print(sq)

output: 0
1
4
9

252
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Loop Bodies (Blocks)
à block can contain any valid Python code
à if…else…
à another loop (nested loop)
for i in range(1, 4):
for j in range(1, i+1): i = 1
print(i, j, i*j) j in range(1, 1+1)
print('')
1 1 1
i = 2
2 1 2 j in range(1, 2+1)
2 2 4
i = 3
3 1 3 j in range(1, 3+1)
3 2 6
3 3 9
253
FooterLicensed to Kevin Romanteau. Email address: [email protected]
data = [10, 20, 30, -10, 40, -5]

suppose we want to replace any negative value with 0

à we can iterate over the data and test for negative numbers:

for number in data: 10


if number < 0: 20
number = 0 30
print(number) 0
40
0
à but how do we replace -10 and -5 with 0 ?
à easy if we know the index number data[3] = 0
data[5] = 0
à but we don't know that!!
254
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The enumerate Function

enumerate is a function that


à takes an iterable argument
à returns a new iterable whose elements are a tuple consisting of:
à the index number of the original element
à the original element itself

data = [10, 20, 30, -10, 40, -5] (0, 10)


for t in enumerate(data): (1, 20)
print(t) (2, 30)
(3, -10)
à at each iteration t is a tuple (index, element) (4, 40)
(5, -5)
à it can be unpacked!
255
FooterLicensed to Kevin Romanteau. Email address: [email protected]
data = [10, 20, 30, -10, 40, -5]
for t in enumerate(data):
index, element = t
if element < 0:
data[index] = 0

data à [10, 20, 30, 0, 40, 0]

à but we can do one better à we can unpack in the for clause itself

for index, element in enumerate(data):


if element < 0:
data[index] = 0

256
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

257
FooterLicensed to Kevin Romanteau. Email address: [email protected]
while Loops

258
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à different than for
à here we want to repeat some code as long as some condition is True
à non-deterministic à we don't necessarily know when condition becomes True
à maybe never! à infinite loop

while expr:
<code block>

à expr is evaluated at the start of each iteration


à if it is True, execute <code block>
à if it is False, terminate loop immediately

à may never execute (if expr is False on first iteration)


à may never terminate (if expr never becomes False)
259
FooterLicensed to Kevin Romanteau. Email address: [email protected]
value = 10

while value < 15: increments value by 1


print(value)
value = value + 1

output: 10
11
12
13
14

260
FooterLicensed to Kevin Romanteau. Email address: [email protected]
value = 100

while value < 15:


print(value)
value = value + 1

output: no output

261
FooterLicensed to Kevin Romanteau. Email address: [email protected]
value = 10

while value < 15: decrements value by 1


print(value)
value = value - 1

output: 10
9
8
7
6
… infinite loop!!

262
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

263
FooterLicensed to Kevin Romanteau. Email address: [email protected]
continue, break, else

264
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Skipping an Iteration
à sometimes we want to skip an iteration, but without terminating the loop
à continue
à immediately jumps to the next iteration

my_list = [1, 2, 3, 100, 4, 5]

for i in my_list: 1
if i > 50: 2
continue 3 à when i is 100
print(i) 4
à continue is executed
print('done') 5
'done' à loop jumps to next iteration

265
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à continue is not used too often
à can sometimes make code difficult to read/understand

for i in my_list:
if i > 50:
continue
print(i)
print('done')

à equivalently: for i in my_list:


if i <= 50:
print(i)
print('done')

à less code, easier to read/understand

266
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Early Termination

à loops can be exited early (before all elements have been iterated)
à break

my_list = [1, 2, 3, 100, 4, 5]

for i in my_list: 1
if i > 50: 2
break 3
print(i) 'done'
print('done')
à when i is 100
à break is executed
à loop is terminated immediately
267
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Early Termination

à loop terminating early using break


à sometimes called abnormal or early termination

à sometimes want to execute some code if loop terminated normally


à and different code otherwise (early/abnormal termination)

268
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Example
We are scanning through an iterable,
looking for an element equal to
'Python'
If we find the value, we want to found = False
terminate our scan immediately, and
print 'found', otherwise we want for el in my_list:
to print 'not found' if el == 'Python':
found = True
print('found')
break

if not found:
print('not found')

269
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The else Clause
à Python is really confusing here…

for loops can have an else clause

à but it has nothing to do with the else clause of an if statement

à the else clause of a for loop executes if and only if no break was encountered
in my mind I read it as "else if no break"

for i in range(5):
<code block 1>
else: # if no break
<code block 2>
à <code block 2> executes if loop terminated normally
(i.e. no break encountered)
270
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Back to our Example
found = False

for el in my_list:
if el == 'Python':
found = True
print('found') equivalently:
break for el in my_list:
if el == 'Python':
if not found: print('found')
print('not found') break
else: # if no break
print('not found')

271
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

272
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Intro to Python for Financial Services

Dictionaries

8 273
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Dictionaries are one of the most important data structures in Python

à we don't always see them


à but they're lurking in the shadows! 😎

à we saw that variables are symbols pointing to objects


à some string (variable name) is associated with some object

objects are also dictionaries


à properties are symbols associated to some value (object)

à methods are names associated to some function


s.upper() l.append()

274
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à associating two things together is extremely useful
a phone book à associates a number to a name
DNS à associates a URL with a numeric IP address
book index à associates a chunk of text with a page number

à associative arrays à sometimes called a map


à abstract concept
à can be implemented in different ways
à Dictionaries (or hash maps) are one concrete implementation

275
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Associative Arrays and Dictionaries

276
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Associating Things
à ASCII table à associates a numeric value to certain characters

A à 65 a à 97 space à 32
B à 66 b à 98 < à 60
… … @ à 64
Z à 90 z à 122 …

We could try this:


keys = [' ', '<', '@', 'A', 'B', …, 'Z', 'a', 'b', …, 'z']
values = [32, 60, 64, 65, 66, …, 90, 97, 98, …, 122]

à to find the numerical value of 'A'


à scan keys to find index of 'A'
à lookup the value for that index in the values list (array)
277
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Another Approach
instead of storing the data in separate lists, use a list of tuples
à each tuple has two elements à (key, value)

items = [('A', 65), … , ('Z', 90), ('a', 97), … , ('z', 122)]

à to find value associated with 'a'


à scan items looking at first item of tuple until we find 'a'
à the value we want is the second element of that tuple we just found

à both approaches have one major drawback


à must scan an array until we find the correct element
à the longer the array, the longer time this will take (worst case is last element)
278
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Hash Maps (aka Dictionaries)
à better implementation is the hash map (or dictionary)

à similar to the last approach we saw

à but a special mechanism is used to quickly find a key


à lookup speed is not affected by size of dictionary

IMPORTANT à keys must be hashable (hence the term hash map)


à what that means exactly is not important now
à strings are hashable à numerics are hashable
à tuples may be hashable (if all the elements are themselves hashable)
à lists are not hashable (in general, mutable objects are not hashable)

279
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Python Dictionaries
à a dictionary is a data structure that associates a value to a key
à both value and key are Python objects
à key must be hashable type (e.g. str, int, bool, float, …) and unique
à value can be any type
à type is dict
à it is a collection of key: value pairs
à it is iterable
à but it is not a sequence type
à values are looked up by key, not by index
à technically there is no ordering in a dictionary
(we'll come back to this point!)
à it is a mutable collection
280
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Dictionary Literals
à dictionaries can be created using literals

d = {'a': 97, 'b': 98, 'A': 65, 'B': 66, 'z': 122, 'Z': 90}

à we can use a single line, but often we structure it over multiple lines to
make it more readable
à readability matters!
d = {
'a': 97,
'b': 98,
'A': 65,
'B': 66,
'z': 122,
'Z': 90
}
281
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Looking up values in a Dictionary

à use [] just like for sequence types


à but instead of an index value we specify the key

d = {
'a': 97,
'b': 98, d['a'] à 97
'A': 65,
d['Z'] à 90
'B': 66,
'z': 122,
'Z': 90
}

282
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Replacing the Value of an existing Key

d = {
'symbol': 'AAPL',
'date': '2020-03-10',
'close': 285
}

To change the value associated to the key 'close':

d['close'] = 285.34

à dictionary now looks like this: d = {


'symbol': 'AAPL',
'date': '2020-03-10',
'close': 285.34
}
283
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Adding a New key:value Pair
à simply assign a value to a new key
à if key exists, it will be updated as we just saw
à if key does not exist, a new entry is inserted with key and value
(and this explains why keys in a dictionary are necessarily unique!)
d = {
'symbol': 'AAPL',
'date': '2020-03-10',
'close': 285.34
} d = {
'symbol': 'AAPL',
'date': '2020-03-10',
d['open'] = 277.14 à
'close': 285.34,
'open': 277.14
}
284
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Deleting a key:value Pair

à we can remove key:value pairs from a dict


à use the del keyword

d = {
'symbol': 'AAPL',
'date': '2020-03-10',
'close': 285.34,
'open': 277.14
} d = {
'symbol': 'AAPL',
del d['open'] à
'date': '2020-03-10',
'close': 285.34}

285
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Common Exceptions

certain operations on dictionaries can lead to KeyError exceptions

à trying to read a non-existent key


à trying to delete a non-existent key

trying to use a non-hashable object as a key leads to a TypeError exception

d[[10, 20]] = 100

à TypeError: unhashable type: 'list'

à [10, 20] is a list, and lists are not hashable


à cannot be used as a key
286
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

287
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Iterating Dictionaries

288
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Dictionaries are Iterable

à means we can use a for loop to iterate over… what?


keys? values? key:value pairs?

à turns out, any of the above

à default iteration is over the dictionary keys

data = {'a': 1, 'b': 2, 'c': 3}

a
for k in data:
à b
print(k)
c

289
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Iterating over values

à dictionaries have a method called values()


à values() returns an iterable containing just the values of the dictionary

data = {'a': 1, 'b': 2, 'c': 3}

1
for v in data.values():
à 2
print(v)
3

290
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Iterating over key:value Pairs
à dictionaries have a method called items()
à items() returns an iterable containing the keys and values in a tuple

data = {'a': 1, 'b': 2, 'c': 3}


('a', 1)
for t in data.items():
à ('b', 2)
print(t)
('c', 3)

à remember unpacking?
a = 1
for k, v in data.items()
à b = 2
print(f'{k} = {v}')
c = 3

291
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The keys() Method
Technically there is also a keys() method

à behaves like values() or items()


à but it is an iterable over the keys of the dictionary

data = {'a': 1, 'b': 2, 'c': 3}

a
for k in data.keys():
à b
print(k)
c

à but default iteration is over the keys anyway


à so keys() is not particularly useful for iteration

292
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Insertion Order
à we saw in sequence types that elements have positional order
à not every iterable has positional order
à we can pull marbles out of a bag, but there is no particular order

à for a long time Python dictionaries were the same


à a "bag" of key:value pairs that could be looked up by key
à iteration order was not guaranteed to be anything specific

à changed in Python 3.6

à the iteration order reflects the insertion order

293
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Insertion Order
what does insertion order mean?

d = {'z': 100, 'a': 1, 'b': 2}

à literal: insertion order is the order in which the key:value pairs are listed out

à 'z': 100, 'a': 1, 'b': 2

à adding a new element d['x'] = 98


à 'x': 98 was added last, so right now it's the "last" element
à 'z': 100, 'a': 1, 'b': 2, 'x': 98

à but we still cannot retrieve elements by index

294
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

295
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Working With Dictionaries

296
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Membership Testing
à can test if a key exists in a dictionary using in

d = {'a': 1, 'b': 2}

'a' in d à True

'x' in d à False

à not in can be used to test if a key is not present

297
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Useful Methods and Functions

d.clear() à removes all elements from d

d.copy() à creates a shallow copy of d


à same as sequences
à use copy.deepcopy() to create a deep copy

len(d) à returns number of elements in d

298
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Other Methods to Create Dictionaries
d = {'a': 1, 'b': 2}
d = dict(a = 1, b = 2)
à symbols must be valid variable names and will be used, in string
form, as the keys

à can create a dictionary with several keys all initialized to the same value
d = dict.fromkeys(['cnt_1', 'cnt_2', 'cnt_3'], 0)
d à {'cnt_1': 0, 'cnt_2': 0, 'cnt_3': 0}

à first argument of fromkeys() should be an iterable (list, tuple, string, etc)


d = dict.fromkeys('abc', 100)
d à {'a': 100, 'b': 100, 'c': 100}
299
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Creating Empty Dictionaries
à often we create dictionaries that start empty
à and get mutated (modified) as our code runs

à can use a literal d = {}

à can use the dict() function d = dict()

300
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The get() Method
à trying to retrieve a non-existent key results in a KeyError exception

à sometimes we want to have a "default" value if a key does not exist


à could use if statements and test is key exists using in
à could try to retrieve the key and handle the exception
à or, use the get() method

get() can take two arguments


à the key for which we want the corresponding value
à the default value we want to use if key does not exist

à get() can take a single argument, the key


à default value is None (special object to indicate "nothing")
301
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The get() Method

d = {'length': 10, 'width': 20}

d.get('length', 0) à 10
the key exists, so the corresponding value (10) is returned

d.get('height', 0) à 0
the key does not exist, so the default (0) is returned

d.get('height') à None
the key does not exist, so the default default-value (None) is returned

302
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The get() Method

à data in Python is often handled using dictionaries

when we work with data we often have missing values


sometimes, not only is the value missing, but the key as well

à using get() allows us to simplify our code to assign a default for missing keys

if 'ssn' in person_dict:
social = person_dict['ssn']
else:
social = ''

à social = person_dict.get('ssn', '')

303
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Merging one Dictionary into Another
à the update() method
à takes a single argument: another dictionary

d1.update(d2)
the key:value pairs of d2 will be merged into d1

à keys in d2 not in d1 will be added to d1 (with the value)


à keys in d2 that are present in d1 will overwrite the value
in d1 with that of d2
à Important: d1 is mutated

304
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Merging one Dictionary into Another

d1 = {'a': 1, 'b': 2}

d2 = {'b': 30, 'c': 40}

d1.update(d2) d1 à {'a': 1, 'b': 30, 'c': 40}

d2.update(d1) d2 à {'b': 2, 'c': 40, 'a': 1}

305
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

306
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Intro to Python for Financial Services

Sets

9 307
FooterLicensed to Kevin Romanteau. Email address: [email protected]
What are Python sets?
à just like a mathematical set
à a collection of elements
à no ordering to the elements
à each element is unique

à it is an iterable
à but no guarantee on what the iteration order will be

think of it like a bag of marbles (a collection of marbles)


to iterate you reach in the bag and grab a marble (any marble)
continue doing so until the bag is empty
à no order guaranteed! à each marble is unique!
308
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Just like mathematical sets, Python supports set operations
à union

à intersection

à difference

à membership (is some object an element of a set or not)

à containment (subset, strict subset, superset, strict superset)

à if you're a little rusty on sets, you should brush up before proceeding


with this section

309
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Python Sets

310
FooterLicensed to Kevin Romanteau. Email address: [email protected]
think back to keys in a dictionary

à they are unique


à they are iterable
à they have no particular order (well, Python 3.6 maintains insertion order)
à keys can be added or removed (dictionary is mutable)
à they are hashable too - but leave that aside for a moment

does that remind you of a set?


à we can think of the keys in a dictionary as a set

à Python's implementation of sets is essentially like a dictionary


à but no values, only keys
à because of this, set elements must be hashable too
311
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Python Sets
à type is set

à sets are iterable

à iteration order is not guaranteed (at least not yet)

à set elements must be hashable

à sets are mutable


à sets are not hashable
à a set cannot be an element of another set, or a key in a dictionary
à if you really want nested sets, use frozenset
à immutable equivalent of sets – those are hashable
(if all the elements are, themselves, hashable)
312
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Defining Sets

à literal form {1, 'a', True}

à note the {} – just like for dictionaries


à but no key:value pairs, just the "keys"

à can also use the set() function


set([1, 'a', True])

à empty set à cannot use {}


à that would be an empty dictionary
à set()

313
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Defining Sets
à can also make a set from any iterable (of hashable elements)

l = [1, 2, 3, 4, 5]
s = set(l) s à {1, 2, 3, 4, 5}

l = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5]
s = set(l) s à {1, 2, 3, 4, 5}

s = set('python') s à {'p', 'y', 't', 'h', 'o', 'n'}

s = set('parrot') s à {'p', 'a', 'r', 'o', 't'}

314
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à use a for loop for iteration

à use in for membership testing

à len(s) returns the number of elements in the set

à s.clear() removes all the elements of the set

à s.copy() creates a shallow copy

315
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

316
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Common Set Operations

317
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Disjointedness

à two sets are disjoint if they have no elements in common

s1.isdisjoint(s2)
à True if no common elements exist
à False if one or more common elements exist

(two elements a and b are considered the same if a == b is True)

318
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Adding and Removing Elements

s = {10, 'b', True}


s.add(4) s à {10, 'b', True, 4}
s.add('b') s à {10, 'b', True, 4} à no duplicates
in a set

s.remove('b') s à {10, True, 4}

s.remove(100) à KeyError

s.discard(4) s à {10, True}


s.discard(100) à no exception s à {10, True}

319
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Subsets and Supersets
s1 < s2 à True if s1 is a strict subset of s2
s1 <= s2 à True if s1 is a subset of s2
s1 > s2 à True if s1 is a strict superset of s2
s1 >= s2 à True if s1 is a superset of s2

{1, 2} < {1, 2, 3} à True {1, 2} < {1, 2} à False


{1, 2} <= {1, 2, 3} à True {1, 2} <= {1, 2} à True

{1, 2, 3} > {1, 2} à True {1, 2} > {1, 2} à False


{1, 2, 3} >= {1, 2} à True {1, 2} >= {1, 2} à True

320
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Unions and Intersections
s1 | s2 à returns the union of s1 and s2
𝑠. 𝑠/

s1 & s2 à returns the intersection of s1 and s2


𝑠. 𝑠/
s1 = {1, 2, 3}
s2 = {3, 4, 5}

s1 | s2 à {1, 2, 3, 4, 5}

s1 & s2 à {3} (again, set elements are unique)

321
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Set Difference
the difference s1 – s2 of two sets is
all the elements of one set minus the 𝑠. 𝑠/
elements of the other set

à caution: set difference is not commutative s1 – s2 ≠ s2 – s1

s1 = {1, 2, 3}
s2 = {3, 4, 5}

s1 - s2 à {1, 2}
s2 - s1 à {4, 5}

322
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à always keep sets in mind when coding

à membership testing with sets is much faster than lists or tuples

à easy to eliminate duplicate values from a collection

à easy to find common values between two collections

à easy to find values in one collection but not in another

323
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

324
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Intro to Python for Financial Services

Comprehensions

10 325
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à comprehensions are an easy way to create new iterables from other iterables

à like using a loop, but easier and more concise syntax

à works well for simple cases


à can quickly become unreadable!
à readability matters!!

Example
given a list of 2D vectors
[(0, 0), (1, 1), (1, 2), (3, 5)]
create a new list containing the magnitude of each vector
à [02 + 02, 12 + 12, 12 + 22, 32 + 52]
à [0, 2, 5, 34]
326
FooterLicensed to Kevin Romanteau. Email address: [email protected]
List Comprehensions

327
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à a comprehension is a way to use one iterable to create another

à more concise than using regular for loops

à use for simple computations


à comprehensions can quickly become confusing

à different types of comprehensions


à lists
à dictionaries
à sets
à generators

328
FooterLicensed to Kevin Romanteau. Email address: [email protected]
List Comprehensions
a list comprehensions is used to generate a list object

Example

we start with an iterable of numbers:

num = (1, 2, 3, 4, 5) or num = [1, 2, 3, 4, 5]

want to create a new list containing the square of each element

sq = [1, 4, 9, 16, 25]

329
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à can do this without comprehensions

numbers = (1, 2, 3, 4, 5) this is the new list we want


sq = [] to create

for number in numbers: loop through every element


of the numbers iterable
sq.append(number ** 2)
calculate the square of the
number and append it to
the new list

sq à [1, 4, 9, 16, 25)

330
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à or we can use a comprehension

numbers = (1, 2, 3, 4, 5)

[] indicates we are creating a list

sq = [number ** 2 for number in numbers]

an expression used to iteration over existing iterable


calculate each element - note how the loop variable is
of the new list available in the expression to the
right

à in general [expression for item in iterable]

331
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à comprehensions offer a more concise (and more efficient!) way of
creating one iterable from another

à in terms of result, these two things do the same

sq = []
for number in numbers:
sq.append(number ** 2)

sq = [number ** 2 for number in numbers]

à comprehensions are actually functions


à builds up and returns the calculated iterable

332
FooterLicensed to Kevin Romanteau. Email address: [email protected]
what about something like this?

given an iterable of integers


à generate a new list that only contains the even integers

numbers = [1, 2, 3, 4, 5, 6, 7, 8]

à generate evens = [2, 4, 6, 8]

333
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à can use a "standard" approach

numbers = [1, 2, 3, 4, 5, 6, 7, 8]
evens = []
for number in numbers:
if number % 2 == 0:
evens.append(number)

à comprehension syntax supports an if clause

evens = [number for number in numbers if number % 2 == 0]

à in general

[expression1 for item in items if expression2]

optional – acts like a filter


334
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

335
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Dictionary/Set Comprehensions

336
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à similar to list comprehensions

à use {} instead of []

à remember literals for dictionaries and sets use {}

à dictionary elements are pairs à key:value

à set elements are single values

d = {'a': 1, 'b': 2, 'c': 3}

s = {'a', 'b', 'c'}

337
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Dictionary Comprehension

{key: value for item in items if expr}

can be any valid Python expression that


calculates some value

can be any Python expressions that


calculates a valid key

338
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Example
Given two lists one of which contains widget names, the other containing
the sales number for each of those widgets – ordered the same
widgets = ['widget 1', 'widget 2', 'widget 3', 'widget 4']
sales = [10, 5, 15, 0]
à create a dictionary whose keys are the widget names, and the value
the number of sales, but only include widgets that had sales.

à "traditional" approach
d = {}
for i in range(len(widgets)):
if sales[i] > 0:
d[widgets[i]] = sales[i]

339
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Example

à or, we can use a comprehension instead


widgets = ['widget 1', 'widget 2', 'widget 3', 'widget 4']
sales = [10, 5, 15, 0]
d = {
widgets[i]: sales[i] à later we'll see an
for i in range(len(widgets) even easier way to do
if sales[i] > 0) this
}
à compare to "traditional" approach
d = {}
for i in range(len(widgets)):
if sales[i] > 0:
d[widgets[i]] = sales[i]
340
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Set Comprehensions

à similar to a dictionary comprehension

à but elements are not key: value pairs

à just the "key" portion

{expr1 for item in items if expr2}

can be any valid Python expression that


calculates some value

341
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Example

Given a list of integers, create a set that contains a unique collection of the
squares of just the even integers

numbers = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6]

s = {number ** 2
for number in numbers
if number % 2 == 0
}

342
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

343
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Intro to Python for Financial Services

Exceptions

11 344
FooterLicensed to Kevin Romanteau. Email address: [email protected]
What are exceptions?

à exceptions are special events that happen when something out of the
ordinary happens while our code is running

à an exception is generally unexpected behavior

à but not always

à it may be something we expect to happen from time to time

à we can deal with it and continue running our code

à so an exception is not necessarily an error

à but unhandled exceptions will cause our program to terminate

345
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Terminology

exception à a special type of object in Python

raising à starting an exception event flow

exception handling à interacting with an exception flow in some manner

unhandled exception à an exception flow that is not handled by our code

à generally results in our program terminating abruptly

346
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Exception Hierarchy
à Python exceptions form a hierarchy
(we'll cover what that means precisely when we look at
Object Oriented Programming - OOP)
https://docs.python.org/3/library/exceptions.html#exception-hierarchy

à basically means that exceptions can be classes sub-divided into sub-


exceptions that are more specific

à for example a broad exception might be LookupError


à more specifically it could be an IndexError or a KeyError
à both of these are categorized more broadly as a LookupError
à can choose to handle IndexError specifically
à or LookupError more broadly
347
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Exception Hierarchy
this means that if an exception object is an IndexError exception
à it is also a LookupError exception
à and it is also an Exception exception
(most exceptions we work with are classified as Exception types)

à confused? think of it this way…

Say we have these classes of objects:


College Person
à a Teacher is also Staff Member
Student Staff Member
à a Staff Member is also a College Person
Administrator
Teacher
348
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Exception Hierarchy
à similarly we have an exception hierarchy
Exception

LookupError
IndexError
KeyError à can even write custom exception types
OSError à later
FileNotFoundError
NotADirectoryError

à so to handle an IndexError, we could choose to


à handle IndexError exceptions very specific
à handle LookupError exceptions
à handle Exception exceptions very broad
349
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Python Built-In Exceptions
à Python has many built-in exception types
https://docs.python.org/3/library/exceptions.html

Common exceptions include:


à SyntaxError
à ZeroDivisionError
à IndexError
à KeyError
à ValueError
à TypeError
à FileNotFoundError à and many more…
350
FooterLicensed to Kevin Romanteau. Email address: [email protected]
EAFP vs LBYL

à when we think something unexpected may go wrong in our code

à figure out if something is going to wrong before we do it


à LBYL Look Before You Leap

à just do it, and handle the exception if it occurs

à EAFP Easier to Ask Forgiveness than Permission

generally in Python à follow EAFP

à exception handling

351
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Why EAFP?
Something that is exceptional should be infrequent

à if we are dividing two integers in a loop that repeats 1,000 times

à out of every 1,000 times we run, we expect division by zero to occur 5 times
LBYL à test that divisor is non-zero 1,000 times

EAFP à just do it, and handle the division by zero error 5 times
à often more efficient

à also trying to fully determine if something is going to go wrong is


a lot harder to write than just handling things when they do go
wrong

352
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Exception Handling Flow

à an exception occurs
à an exception object is created
à an exception flow is started

à we do nothing about it
à program terminates

à we intercept the exception flow


à try to handle the exception in some sense, if possible
à then
à resume running program uninterrupted
à or, let the exception resume
à or, start a new exception flow
353
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Raising Exceptions

354
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à often we want to start an exception flow ourselves

à called raising an exception

à an exception object is associated with an exception flow

à we create a new exception object


à we raise the exception object

à doing this is most useful when we create functions


à we'll see this later

à for now, we'll just learn how to raise an exception

355
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Example

à create an exception object using one of Python's built-in exceptions


ex = ValueError()

à usually we include a custom exception message

ex = ValueError('Name must be at least 5 characters long.')

à we raise the exception, starting an exception flow


raise ex

à often do both in one step

raise ValueError('custom message')

356
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à raising an exception ourselves results in the same exception flow that
Python does when it raises some exception

à we can choose to handle the exception


à if we don't handle the exception, program terminates

357
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

358
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Handling Exceptions

359
FooterLicensed to Kevin Romanteau. Email address: [email protected]
General Suggestions for Exception Handling
à in general we do not want to just handle any exception anywhere in our code
à too much work
à cannot anticipate every point of failure
à it's OK for program to terminate - we can figure out what went
wrong and attempt to fix it later – possibly handling that case
specifically
à if we don't know exactly why or where the problem occurs in our
code, there's not much we can do to recover from the exception

à we handle exceptions that are raised by small chunks of code


à we try to handle very specific exceptions, not broad ones
à usually handle exceptions that we can do something about

360
FooterLicensed to Kevin Romanteau. Email address: [email protected]
try…except…
à wrap the code we want to implement an exception handler for
inside a try block
à we handle possible exception(s), using except blocks (one or more)

a = 1 this gives us the exception


object that was raised – we can
b = 0
assign it to any symbol we want
try: – here I just chose ex
result = a / b
except ZeroDivisionError as ex:
print(f'Exception occurred: {ex}') do something to handle
result = 0 the exception

print(result) because we handled the exception, the flow


was interrupted and our code continues to
run normally

361
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Handling and re-raising an exception

à sometimes we want to handle an exception, but then re-raise the same


exception or a different exception
à often because there's nothing we can do
à sometimes to create a more explicit exception

to raise an exception in an except block:

raise à re-raises the same exception that caused the except block to be entered

raise SomeException('…') à raises a new exception

362
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Application

à one very common case for re-raising exceptions is for error logging

à we can view the logs after our program has terminated abnormally
try:

except Exception as ex:
log(ex)
raise

à we intercept a broad range of exceptions by handling Exception


à we log the exception somewhere (console, file, database, etc)
à we re-raise the exception and let something else either handle it
or terminate the program

363
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à what do I mean "something else handles it"?
à we'll see this more when we cover functions

à try…except… can be nested à usually indirectly


à but directly too
try:
try:
raise ValueError('something happened')
except ValueError as ex:
log(ex)
raise
except Exception as ex:
print(f'ignoring: {ex}')

à here our ValueError gets handled twice!

364
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Handling Multiple Exception Types
à not limited to a single except block

try:

except IndexError as ex:

except ValueError as ex:

except Exception as ex:

à Python will match the exception to the first type that matches in
sequence of except blocks

à so write except blocks from most specific to least specific exception types
à remember that exception hierarchy we looked at!
365
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The finally Clause

à sometimes we want some code to run after a try…except… whether


an exception occurred or not, and whether it was handled or not
à use the finally clause

try:

except ValueError as ex:

except IndexError as ex:

finally:
# always runs no matter what, before exception flow resumes

366
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Application
à useful when we want a piece of code to always run
à whether an exception has occurred or not
à whether the exception was handled or not
à whether exception was re-raised or a new one raised

try:
open_database_connection()
start_transaction()
write_data()
commit_transaction()
except WriteException as ex:
rollback_transaction()
raise
finally:
close_database_connection()
367
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

368
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Intro to Python for Financial Services

Iterables and Iterators

12 369
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à an iterable is something that can be iterated over
à i.e. we can take one element, then the next, then the next, until
we've covered all elements
à no specific iteration order is mandated

à obviously a sequence type is iterable (positional ordering)


à we saw dictionaries can be iterated over (insert order)
à but we also so sets: iterable, but no guaranteed order of any kind
à general idea behind iteration is then:
à start somewhere in the collection (at the beginning if that means something)
à keep requesting the next element
à until there's nothing left (exhausted)

370
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à so we have two concepts here

à a collection of objects that we can iterate over


à an iterable

à something that is able to give us the next element when we request it


à an iterator

à we are going to look at those in this section

371
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Iterables and Iterators

372
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à an iterable is something that can be iterated over

à but we still need something that can

à give us the next item

à keep track of what it's given us so far (so it does not give us the same
element twice)

à informs us when there's nothing left for it to give us

à this is called an iterator

à used by Python to iterate over an iterable

373
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à an iterable is just a collection of objects
à it doesn't know anything about how to iterate
à however it knows how to create and give us an iterator when we need it

à iterables implement a special method __iter__() that returns a new iterator


à can also be called by using the iter() function

à the iterator has a special method called __next__() that can be called
to get the next element
à can also use the next() function

à it keeps track of what it has already handed out


(so iterators are kind of one time use!)
à it raises a StopIteration exception when next() is called if
there's nothing left
374
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The Internal Mechanics of a for Loop
When we write a for loop that iterates over an iterable, what Python is actually
doing this:

l = [1, 2, 3, 4, 5]

iterator = iter(l)
try:
while True:
# return next(iterator) – here we'll just print it
print(next(iterator))
except StopIteration:
# expected when we reach the end
# so silence this exception
pass

375
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à the key thing here is that we can see the iterator has some state
à it has a __next__() method
à but there's no going back, or starting from the beginning again
à to do that we have to request a new iterator

à and that's what a for loop does – it requests a new iterator from the iterable
before it starts looping
à objects such as lists, tuples, string, dictionaries, sets, range objects are iterables
à but some objects in Python are iterators – not iterables
à iterators actually implement an __iter__ method
à but they just return themselves (with their current state), not a new iterator
à they allows us to iterate over them
à but only once
376
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

377
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Generators

378
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à we've seen list, dictionary and set comprehensions

à but no tuple comprehensions…


result = [i ** 2 for i in range(5)]

result = []

for i in range(5):
result.append(i ** 2)

à works because list is mutable


à tuples are not mutable

à no tuple comprehension
379
FooterLicensed to Kevin Romanteau. Email address: [email protected]
so what does this (valid) expression do?
(i ** 2 for i in range(5))

à creates a generator object


à generators are iterators à next()
à they calculate and hand out elements one at a time as requested

à unlike [i ** 2 for i in range(5)]


à calculates all the elements and creates the list immediately

à generators use lazy iteration

à a lazy property is one that is not calculated until it is requested

380
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Why use generators?
à memory efficiency
à e.g. take all the rows from a file, and write them out, transformed
to some other file

à read the entire file in memory, iterate through that and save rows
à entire file in memory!
à you may not have enough memory!

à read lines one at a time from file


à read a row, process it, save it, discard it, request next row, …
à only one line in memory at any point

381
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Why use generators?
à performance (possibly)

à if you only need to read the first few elements of the iterable

à why go through the computations to calculate all of them?


à plus unnecessary memory usage on top of that

382
FooterLicensed to Kevin Romanteau. Email address: [email protected]
What's the downside of generators?
à generators are lazy iterators
à one-time use

à not good if you need to iterate through the same iterable many times

à or even just a few times if the calculations are computationally


expensive or take a long time (maybe IO bound)

383
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Creating Generators

à use generator comprehension

à use the yield keyword in functions instead of return


à beyond scope of this course

384
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

385
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Intro to Python for Financial Services

Functions

13 386
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à we have used functions a lot so far

print()

iter()

next()

list()

math.sqrt()

and many more…

à we can create our own, custom, functions

387
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Why?

à easy code re-use


à much easier to code the sqrt() function once
à and then call it multiple times

à breaking up complex code into easier to understand chunks

à problem decomposition

388
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à when we create a function, we may also want values to be passed into
it when it is called

à arguments or parameters

à technically not the same thing, but almost


everyone uses them interchangeably à as do I L

when we define a function we may


define symbols for the values that à these symbols are called parameters
will be passed to the function

when we call a function we specify


à these values are called arguments
values for these parameters
à so a parameter is when we define the function
à an argument is when we call the function 389

FooterLicensed to Kevin Romanteau. Email address: [email protected]


Functions are Python Objects
à just like everything in Python, functions are objects
à they have state
à name (maybe!)
à code
à parameters
à they are callable à and always return something when called

à they can be assigned to a symbol


à can be passed as a parameter to another function
à can be returned from a function call

390
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Callables
à an object is callable if it can be called – using ()

à functions are run by calling them print('hello')


math.sqrt(4)

à but other types of objects are also callable

à not necessarily a function object

my_list.copy() à calling a method on the my_list object


range(100) à creating a new range object

à more general term is a callable

391
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Custom Functions

392
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à functions can be defined using the def keyword

def keyword indicates a function is being defined


function's name can be any valid
Python name (just like variables)
def function_name():
# indented block
… this block is called the function body
return <value>
functions always return some value

à function body contains any valid Python code


à this creates a function object
à the function object is associated with the symbol function_name
(in the same way a = 10 associates the integer object 10 with the symbol a)

393
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Example

def say_hello():
print('Hello!')

say_hello() à Hello!
say_hello() à Hello!

but no return?

à if a return value is not specified, function will return None

394
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Example

def one():
return 1 function returns the value 1 when it is called

result = one() à assigns the return value of calling one() to the


symbol result

395
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à usually functions contain a little more complex code

from datetime import datetime

def current_time_utc():
return datetime.utcnow().isoformat()

result = current_time_utc()

result à "2020-03-31T02:44:38.490923"

396
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à functions are usually more helpful when we can pass values to them
len(my_iter)

we are passing an argument to the len function

à every time we call the len function we can pass a different value
à the function body (implementation) of the len function starts running

à it is aware of the value that was passed to it

à same with custom functions


à need to specify the parameters, by name, that will be used when we call it

def add(a, b): add(2, 3) à 5


return a + b add(10, 1) à 11

397
FooterLicensed to Kevin Romanteau. Email address: [email protected]
def subtract(a, b):
return a - b

subtract(10, 7) à 3

à when we call subtract(10, 7), how does Python assign 10 to


the symbol a, and 7 to b?

à it does this by position

def my_func(a, b, c, d): my_func(10, 20, 30, 40)


à positional arguments

398
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Namespaces
à when a function is called
à it knows nothing about how it was called before

à every time a function is called

à an empty dictionary is created


à populated with any arguments passed in
à key = param name, value=argument
à nothing else

à then the function code runs


à this dictionary is called the (local) namespace

399
FooterLicensed to Kevin Romanteau. Email address: [email protected]
abs_max(1, -2)
def abs_max(a, b): {'a': 1, 'b': -2}
abs_a = abs(a) {'a': 1, 'b': -2, 'abs_a': 1}
abs_b = abs(b) {'a': 1, 'b': -2, 'abs_a': 1, 'abs_b': 2}
if abs_a > abs_b:
max_val = abs_a
else:
{'a': 1, 'b': -2, 'abs_a': 1, 'abs_b': 2,
max_val = abs_b 'max_val': 2}
return max_val

à after function return, dictionary is wiped out

à consecutive calls to the same function are independent of each other

400
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

401
FooterLicensed to Kevin Romanteau. Email address: [email protected]
* Arguments

402
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à saw how to specify positional parameters in a function

def average(a, b, c, d):


return (a + b + c + d)/4

à but what if we wanted to specify an arbitrary number of parameters?


à we'd like to call our function with different number of args

average(1)
average(1, 2, 3)
average(1, 2, 3, 4)

403
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à could write a function to use an iterable as a single argument

def average(iterable):
return sum(iterable) / len(iterable)

à but this makes the calling syntax a little weird

average([1, 2, 3])
average([1])

à would be nicer if we had a mechanism to accept a variable number of args

404
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à Python supports a special parameter type for this

à uses a * prefix on a parameter name

def average(*values):
# return average
à this means we can call average with any number of arguments
average(1)
average(1, 2, 3, 4, 5)

àhow do we access these values inside the function

à use the parameter name à values in this case

à it will be a tuple containing all the argument values

405
FooterLicensed to Kevin Romanteau. Email address: [email protected]
def average(*values):
print(type(values))
print(values)

average(1, 2, 3) à values will be a tuple


à (1, 2, 3)

def average(*values):
return sum(values) / len(values)

à we may want to do something if someone calls this


function with no arguments

406
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à often you will see code that uses *args

à the * is the important part


à there is nothing special about the name args
à as we just saw, we can use any valid name
à use a meaningful name à args is often too generic

407
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

408
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Default Values

409
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à possible to specify optional parameters

à means function can be called without passing in the argument

à but we still have that parameter

à it needs a value
à we can specify a default value to use if the argument is not supplied

410
FooterLicensed to Kevin Romanteau. Email address: [email protected]
def func(a=1):
print(a)
a default value to use if a is not supplied
when function is called

func() à 1

func(10) à 10

à once you specify a positional parameter with a default value

à all positional parameters after that must specify a default value too

à with the exception of a starred parameter

411
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à how would you interpret this?

def func(a=1, b):


pass

func(10)

à is 10 supposed to go into a? à in which case we're short one argument


à or use default for a and assign 10 to b?

à don't know!

412
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à so once we have default arguments we need to specify default for all
parameters after it

def func(a, b, c=1, d=2):


but this is still ok: def func(a, b=1, *args)

func(10) a à 10 b à 1 args à (,)

func(10, 2) a à 10 b à 2 args à (,)

func(10, 2, 3, 4) a à 10 b à 2 args à (3, 4)

413
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

414
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Keyword-Only Arguments

415
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à we saw how positional parameters can be passed
à positionally
à as a named argument à also called a keyword argument

def func(a, b, c):


func(1, 2, 3)

func(c=3, b=2, a=1)

à passing argument as a keyword argument is optional

416
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à can also make passing an argument by name mandatory

à these are called keyword-only arguments

à keyword-only parameters must come after all positional parameters

def func(a, b, c)

à we want c to always be passed as a named argument

à somehow we have to tell Python that after a and b there are no


more positional arguments

417
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à one way is to use a * parameter

def func(a, b, *args, c)

à since *args will scoop up every remaining positional argument


à c must be a keyword-only argument

func(10, 20, 30, c=100)


a à 10 b à 20 args à (30, ) c à 100

func(10, 20, c=100)


a à 10 b à 20 args à (, ) c à 100

418
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à but this allows someone to pass in as many positional arguments as they want

à what if we don't want that?

want to make this allowed: func(10, 20, c=100)


but not this: func(10, 20, 30, 40, c=100)

à we still have to tell Python that there are no more positional arguments

à we use a * without a parameter name

419
FooterLicensed to Kevin Romanteau. Email address: [email protected]
def func(a, b, *, c):

à a and b are positional parameters

à there are no more positional parameters after that

à so c is a keyword-only argument

func(10, 20, c=100)

func(10, 20, 30, c=100)

420
FooterLicensed to Kevin Romanteau. Email address: [email protected]
def func(a, b, *, c):

à using this technique c must be passed as a named argument


à a and b can be passed as positional arguments
à or as named arguments

func(b=2, a=1, c=3)

func(a, c=3, b=2)

421
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Default Values

à can also assign default values to keyword-only arguments

def func(a, b, *, c=100):


à c is optional, and will default to 100


à if c is passed, it must still be passed as a named argument

func(10, 20) c à 100

func(10, 20, c=30) c à 30

à can mix default values for both positional and keyword-only arguments
422
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Arbitrary Number of Keyword-only Parameters

à saw * for arbitrary number of positional arguments

à use ** for arbitrary number of keyword-only arguments

func(a, b, *args, c, d, **kwargs)

à a and b are positional


à c and d are keyword-only
à extra positional arguments are scooped up into args
à extra named arguments are scooped up into kwargs

423
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à ** keyword-only arguments are scooped up into a dictionary
à key is the argument name
à value is the argument value

def func(a, *, d, **others):


à others is a dict

a à 10
func(10, d=2, x=10, y=20) d à 2
func(a=10, d=2, x=10, y=20) others à {
'x': 10,
func(x=10, y=20, d=2, a=10) 'y': 20
}

424
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

425
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Lambda Functions

426
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à lambda functions are just functions

à they are not defined using a def and block of code

à it is an expression that returns a function object

à Python does not create a symbol or a name for the function

à just returns the function object

à we can assign it to a variable or pass it as an argument

à also called anonymous functions

à they are very simple functions (no code block)

427
FooterLicensed to Kevin Romanteau. Email address: [email protected]
lambda a, b: a + b

function parameters what the function should return


à must be a single expression
à no code block
à so no loops, try…except…, if…else…, etc

à this expression returns a function object

à we need to assign it to a symbol if we want to use it

f = lambda a, b: a + b

f(10, 20) à 30
428
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à can always use a function defined using def instead of these lambdas

à generally used to write shorter code in some simple cases

à we'll see example of this in the next sections

à but you don't have to use them

à however they do get used often, so you should be aware of them

429
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

430
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Intro to Python for Financial Services

Some Built-In
Functions

14 431
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à in this section we are going to look at some more of Python's built-in functions

à there are many more!

à https://docs.python.org/3/library/functions.html

à and that does not even include the thousands of functions available
in Python's standard library

à we'll study some of them later in this course

à math and stats


à time and datetime
à csv
à random and more…
432
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Rounding

433
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à round() is a built-in function that can be used to round floats

à uses banker's rounding


à also called round half to even

à rounds away from zero 1.8 à 2


-1.8 à -2

à ties round to closest even digit 1.5 à 2

2.5 à 2

à good choice to eliminate various biases

434
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à use round() to round to an integer

round(1.8) à2

round(-1.8) à -2

round(1.5) à2

round(2.5) à2

435
FooterLicensed to Kevin Romanteau. Email address: [email protected]
1
à can also use round() to round to closest multiple of
10
round(value, exponent)
1
exponent is used to specify what power of to round to
10
à let's look at it mathematically first (i.e. without worrying about float representations)

round(x, 1) à rounds to nearest 0.1 (104. )

round(x, 2) à rounds to nearest 0.01 (104/ )

round(x, -1) à rounds to nearest 10 (10. )

round(x, -2) à rounds to nearest 100 (10/ )


436
FooterLicensed to Kevin Romanteau. Email address: [email protected]
round to closest
multiple of:

round(127.1892, 3) 10-3 à 0.001 à 127.189

round(127.1892, 2) 10-2 à 0.01 à 127.19

round(127.1892, 1) 10-1 à 0.1 à 127.2

round(127.1892, 0) 100 à 1 à 127.0

round(127.1892, -1) 101 à 10 à 130.0

round(127.1892, -2) 102 à 100 à 100.0

round(127.1892, -3) 103 à 1000 à 0.0

437
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Rounding Ties in Floats
à technically rounds to closest number that ends with an even digit

round(0.125, 2) à 0.12

à so why this?
round(0.325, 2) à 0.33 why not 0.32?

à remember floats do not have (in general) an exact representation!

0.325 à 0.325000000000000011102230246252

à so this is not a tie!

438
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

439
FooterLicensed to Kevin Romanteau. Email address: [email protected]
sorted, min and max

440
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Sorting Numbers
à numbers have a natural sort order
à they can be sorted ascending or descending by that sort order
à sorted is a built-in function that can be used to sort a collection of numbers
à single positional argument: an iterable containing the numbers
à by default, it sorts in ascending order

à keyword-only argument to reverse the sort order


à default is False à sorts ascending
à specify reverse=True à sorts descending
à always returns a new list
à original iterable is not mutated

441
FooterLicensed to Kevin Romanteau. Email address: [email protected]
t = (1, 10, 2, 9, 3, 8)

sorted(t)
à [1, 2, 3, 8, 9, 10]

sorted(t, reverse=True)

à [10, 9, 8, 3, 2, 1]

442
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Sorting Strings
Numbers have a natural sort order

Strings also have a natural sort order in Python


à lexicographic order
à dictionary order, alphabetical order

BEWARE The characters a and A are not the same

à Python assigns a numerical character code (the unicode character


code) to each character in a string

A à 65 Z à 90
à'A' < 'Z' < 'a' < 'z'
a à 97 z à 122
443
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à so Python will use "alphabetical" sorting, but upper case letters will be
sorted before their equivalent lower case versions

à natural sort order of string is case sensitive

sorted(['Boy', 'baby'])

à ['Boy', 'baby'] (ascending sort)

444
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Sorting Other Types

à we can "visually" sort other types of objects

à list of Persons

à we can sort this list

à by name
à by age
à by profession

à we always sort by some property of the objects we are sorting

à we'll come back to this in a later section

445
FooterLicensed to Kevin Romanteau. Email address: [email protected]
min and max
à closely related to sorting

à to find the minimum of a collection


à sort the collection (by something) in ascending order
à pick the first element

à to find the maximum of a collection


à sort the collection (by something) in descending order
à pick the first element

(or you could sort in the other direction in both cases and pick the last element)

446
FooterLicensed to Kevin Romanteau. Email address: [email protected]
min([1, 10, 2, 9, 8]) à 1

max([1, 10, 2, 9, 8]) à 10

min([]) à ValueError exception

à can specify a default value to return if the iterable is empty


à keyword-only argument

min([], default=0) à 0

447
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à can also use an arbitrary number of positional arguments instead

min(1, 10, 2, 9, 3, 8) à 1

max(1, 10, 2, 9, 3, 8) à 10

à we'll come back to min and max when we look at sorting again later in
this course

448
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

449
FooterLicensed to Kevin Romanteau. Email address: [email protected]
The zip() Function

450
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à the zip() function is a very useful and often used function

à consider these two lists that contain related information

l1 = ['a', 'b', 'c', 'd', 'e', 'f']


l2 = [97, 98, 99, 100, 101, 102]

à we want to create a list of tuples that contain the corresponding


elements from l1 and l2

451
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à could do this:
combo = [(l1[i], l2[i]) for i in range(len(l1))]

combo à [('a', 97), ('b', 98), ('c', 99),


('d', 100), ('e', 101), ('f', 102)]

but, we may have an issue if the two lists are not of the same length
àhave to stop at the shortest of the two lengths

l1 = ['a', 'b', 'c', 'd', 'e']


l2 = [97, 98, 99]

combo = [(l1[i], l2[i]) for i in range(min(len(l1), len(l2)))]

combo à [('a', 97), ('b', 98), ('c', 99)]

452
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à that's what the zip() function does!

l1 = ['a', 'b', 'c', 'd', 'e']


l2 = [97, 98, 99]

combo = zip(l1, l2)

BEWARE zip() returns an iterator


à remember those? à can only iterate through them once

list(combo) à [('a', 97), ('b', 98), ('c', 99)]

list(combo) à []

453
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à if you want to iterate multiple times over the same zipped collection

à store it into a list


combo = list(zip(l1, l2))

à often don't need to


zip() does not actually create anything other than an iterator
à no physical space has been used for the tuples

à iterating over zip() result, just iterates over the iterables simultaneously

à costs almost nothing calling zip(l1, l2) multiple times

454
FooterLicensed to Kevin Romanteau. Email address: [email protected]
à zip is extensible

à not limited to two iterables


à any number of iterables (positional args)

l1 = [1, 2, 3]
l2 = [1, 2, 3, 4, 5]
l3 = [1, 2, 3, 4, 5, 6, 7]

zip(l1, l2, l3) à (1, 1, 1)


(2, 2, 2)
(3, 3, 3)

à always returns an iterator that produces tuples

455
FooterLicensed to Kevin Romanteau. Email address: [email protected]
Coding

456
FooterLicensed to Kevin Romanteau. Email address: [email protected]

You might also like