# | include: false
import numpy as np
import pandas as pd
256852) np.random.seed(
Fundamentals (Nice)
The material in this ‘Nice’ chapter is optional. The discussion typically deals with content beyond what a novice should know. So, please finish all the ‘Need’ and ‘Good’ portions before you go through this chapter.
1 If if
is not enough
Sometimes you have multiple conditions you want to check. For such situations, the if
-elif
-else
statements can be cumbersome. Since Python 3.10, you can use a match
-case
statement that goes like this:
= 'Batman'
name
match name:
case 'Batman':
print('Hello Hero | Batman!')
case 'Robin':
print('Hello Sidekick | Robin!')
case _:
print('Hello World!')
2 Ternary operators or Conditional Statements
Python offers ternary operators (containing three parts) that can be useful to make your code more readable and less verbose.
-
So, instead of
if nationality == 'French': = "Bonjour!" greeting else: = "Hello!" greeting
we can write
= "Bonjour!" if nationality == 'French' else "Hello!" greeting
This works too!
"Hello!", "Bonjour!")[nationality == 'French'] (
-
Here is another ternary operator.
= None text = text or "No message!" message
3 Swapping values
If you want to swap the content of two variables, you can do:
= 1, 2
a, b = b, a
a, b print(a, b)
This only works with Python!
4 There are more types
We discussed earlier how we should never check the equality of two floats because it will be influenced by rounding off errors. What if you want to do some absurdly precise calculations? One way around this is to use higher precision types like np.float32
or np.float64
. To demonstrate what these offer let me use np.finfo()
to query the limits applicable to floating point operations. The following gives the difference between 1.0 and the next biggest nearest float for the various types:
= [
my_types float, # Default for core Python on my machine
np.float16,
np.float32,
np.float64,
np.float128
]
for my_type in my_types:
print(f'{my_type.__name__:<15s}:', np.finfo(my_type).eps)
float : 2.220446049250313e-16
float16 : 0.000977
float32 : 1.1920929e-07
float64 : 2.220446049250313e-16
longdouble : 1.084202172485504434e-19
5 Operator precedance
There is a ‘pecking order’ among the various operators in Python. This idea is called operator precedence. Below is a quick summary of how this is set up in Python.
- Highest precedence at the top, lowest at the bottom.
- Operators in the same box evaluate left to right. (Reproduced from this website)
Description | Operator |
---|---|
Parentheses(grouping) | () |
Function call | f(args...) |
Slicing | x[index:index] |
Subscription | x[index] |
Attribute reference | x.attribute |
Exponentiation | ** |
Bitwise not | ~x |
Positive, negative | +x, -x |
Multiplication, division, remainder | *, / ,% |
Addition, subtraction | +, - |
Bitwise shifts | <<, >> |
Bitwise AND | & |
Bitwise XOR | ^ |
Bitwise OR | \| |
Comparisons, membership, identity | in, not in, is, is not, <, <=, >, >=, <>, !=, == |
Boolean NOT | not x |
Boolean AND | and |
Boolean OR | or |
Lambda expression | lambda |
6 Variables in Python are just names
6.1 The Problem
Since we use variables all the time, it is good to understand how they work. This is particularly true for Python because certain Python variables can be sneaky!
What do you think will be printed if you run the following code? Try to predict the answers before running the code.
= [1, 2]
x = x
y 3)
y.append(
print(f"x: {x}, y: {y}")
6.2 An explanation
Let’s explore the root of this by first running the following code:
'''CODE 1'''
'CODE 1'
= 1
x = 1
y
print(f"x: {id(x)}, y: {id(y)}, 1: {id(1)}")
x: 140280593755848, y: 140280593755848, 1: 140280593755848
The above code tells us that x
, y
both have the same id as 1
! The following figure tries to explain what is happening.
Before the code is run, Python has things or objects
1
,2
anda
that have three properties type, value and id. For example,1
can have the value 1, typeint
, and some id.a
can have the value ‘a’, typestr
and some id.After the code is run,
x
andy
are ‘looking at’ or bound to1
. Sox
andy
are referred to as names that are bound to1
1.
Now run this code:
'''CODE 2'''
'CODE 2'
= 1
x = x + 1
y
print(f"x: {id(x)}, y: {id(y)}")
x: 140280593755848, y: 140280593755880
print(f"1: {id(1)}, 2: {id(2)}")
1: 140280593755848, 2: 140280593755880
Since the mathematical operation requires y
to have the value 2
, y
now gets bound to object 2
. This happens because the value of object 1
cannot be changed, so the binding is changed instead.
Objects such as 1
whose values cannot be changed are called immutable. Other such immutable types are str
(i.e., letters), float
, bool
.
There are also objects whose values can be changed. These types are called mutable and include lists
and dictionaries
and instances of classes. These behave differently, as highlighted in the problem above.
Here is the code from the ‘problem’ with some explanations.
# x is bound to a list object with a value [1 ,2]
= [1, 2]
x
# y is bound to the SAME list object with a value [1 ,2]
= x
y
# y is used to change the value of the object from [1, 2] to [1, 2, 3]
3) y.append(
6.3 A solution
If you really want y
to have an independent copy of x
, you should use:
= x.copy() y
Be very careful when you use mutable data types as variables.
7 ==
is not the same as is
Python has several ways of comparing ‘items’. ==
and is
are two examples. Here is the difference between them:
x is y
checks for identity. i. e., it asks if x
and y
are bound to the same object by comparing the ID.
x == y
checks for equality by running a function that checks for equality (such as _eq_
of a class). You will understand more of this as we develop the idea of classes in later chapters.
Footnotes
This is the basis of the, sometimes contentious, statement that there are no variables in Python↩︎