Building Basic Logic in Python¶
Lesson 5: Conditional¶
We often want our programs to take actions based on what's going on at the time (e.g. the current value of some variable). In a Python program, the if...elif...else
statement is how we perform this sort of decision-making. It allows for conditional execution of a line of code or a block of codes based on the value of the conditional expression.
Booleans¶
A conditional expression in Python is evaluated to a boolean, bool
for short. A bool variable can take two values, True
or False
. Python implements all of the usual operators for bool logic using simple English words and
, or
, and not
.
b1 = True
b2 = False
b1 and b2
b1 or b2
not b1
First conditional statement¶
b = True
if b:
print("This will be printed")
b = False
if b:
print("This will not be printed")
What is true?¶
The bool function¶
We can check whether a variable of other data types (e.g. int
, string
, and even list
) will be regarded as True
or False
using the bool()
function. Some of the results in the below examples (e.g. bool[""]
) may not be what you think at first sight. Loosely speaking, for container data types like list
, an empty state will be evaluted to False
.
bool(3)
bool(0.0)
bool("Berkeley")
bool("")
bool([1, 2, 3])
bool([])
bool([0])
bool([""])
No need to explicitly convert using the bool()
function¶
if []:
print("This will not be printed")
if [""]:
print("This will be printed")
Some other conditional expressions¶
==
,!=
,>
,<
in
membership assesmentis
identity operatorin
andis
can be combined withnot
to formnot in
andis not
3 == 2
"H" in "Hydrogen" # character(s) in string
"a" not in "Hydrogen"
2 in [1, 2, 3] # componment in list
element = None
element is None
element = "Hydrogen"
element is not None
The if elif else
statement¶
Let's start with an illustrative example of the state of water based on temperature.
water_temperature = -1 # degrees celsius
Below is the simple if statement: "if the temperature of water is greater than or equal to 100 °C, it must be boiling".
if water_temperature >= 100:
print("Boiling!")
Now, let's add an else
to catch our if statement: "if ... greater than ..., otherwise, the water is not boiling".
if water_temperature >= 100:
print("Boiling!")
else:
print("Not boiling!")
We can increase the number of levels of the elif
statement in the following manner:
if (condition 1):
execute code for case 1
.
.
.
elif (condition k):
execute code for case k
.
.
.
elif (condition n):
execute code for case n
else:
execute "catch all" case
water_temperature = -5
if water_temperature >= 100:
print("Gas!")
elif water_temperature > 0:
print("Liquid!")
else:
print("Solid!")
Lesson 6: Dictionary¶
Dictionary is a data type to store key:value pairs. It maps key to value. Dictionaries are optimized for retrieving data. We must know the key to retrieve the value. A dictionary is ordered (since Python 3.7), changeable, and does not allow duplicates.
Create dict and access value¶
elements = {"Hydrogen": 1, "Carbon": 6}
elements
n = elements["Carbon"]
n
Basic operations on dict (add, edit, and remove)¶
# add via key-value
elements["Copper"] = 27 # oops, seems wrong
elements
# edit
elements["Copper"] = 29 # that's better
elements
# add multiple key value pairs from another dict
elements.update({"Nitrogen": 7, "Oxygen": 8})
elements
# remove by key
v = elements.pop("Nitrogen") # return the value, here 7
print("return value:", v)
print("current dict:", elements)
Dictionary keys should be immutable¶
- Immutable objects: in simple words, an immutable object cannot be changed after it is created. Pyhton built-in data types such as
int
,float
,bool
,string
, andtuple
are immutable. - Mutable objects: a mutable object can be changed after it is created, e.g.
list
anddict
.
elem = "Hydrogen"
elem[2]
# elem[2] = "D" # this gives error; cannot edit a string
# molecule = {["Hydrogen", "H"]: 1} # this gives error; mutable list cannot be dict key
molecule = {("Hydrogen", "H"): 1} # a tuple of string can be used as dict key
Loop over dictionary¶
simple loop over keys¶
elements = {"Hydrogen": 1, "Carbon": 6, "Oxygen": 8}
for k in elements:
v = elements[k]
print("key =", k)
print("value =", v)
loop over key value pairs together¶
for k, v in elements.items():
print("key =", k)
print("value=", v)
More ways to create dictionary¶
names = ["Hydrogen", "Carbon", "Oxygen"]
numbers = [1, 6, 8]
dict comprehension¶
elements = {i: s for i, s in zip(numbers, names)}
elements
dict
function¶
elements = dict(zip(numbers, names))
elements
Lesson 7: Function¶
Break down programs into functions¶
- Readability: human beings can only keep a few items in working memory at a time. Encapsulate complexity so that we can treat it as a single "thing".
- Reusability: write one time, use many times.
- Testing: components with well-defined boundaries are easier to test.
- ...
Define a function using def
with a name, arguments, and a block of code¶
- Function name must obey the same rules as variable names (i.e. combination of alphanumerics and the underschore "_")
- Put arguments in parentheses
- Then a colon, and finally an indented code block
# Empty parentheses if the function doesn't take any arguments
def say_hi():
print("Hi!")
say_hi()
def say_hi_2(name):
print("Hi", name, "!")
say_hi_2("Berkeley")
A Function can return a result to its caller using return
¶
import math
def get_vector_magnitude(x, y):
mag = math.sqrt(x ** 2 + y ** 2)
return mag
m = get_vector_magnitude(3, 4)
print("magnitude of vector:", m)
# A function without explicit `return` returns `None`
print("return value of `say_hi()` is:", say_hi())
Can specify default values for arguments¶
- All arguments with defaults must come after all arguments without (otherwise, argument-to-parameter matching would be ambigious)
- Makes common cases simpler, and signals intent
- Functional can return multiple values
def scale_vector_by_factor(x, y, factor=1):
x *= factor
y *= factor
return x, y
print("scale_vector_by_factor with default:", scale_vector_by_factor(3, 4))
print("scale_vector_by_factor:", scale_vector_by_factor(3, 4, 2))
scaled_x, scaled_y = scale_vector_by_factor(3, 4, 2)
print("scaled x:", scaled_x, "scaled_y:", scaled_y)
Can pass arguments by name¶
- Helpful when functions have lots of options
If you have a procedure with ten parameters, you probably missed some.
-- from "Epigrams in Programming", by Alan J. Perlis
print("scale_vector_by_factor:", scale_vector_by_factor(x=3, y=4, factor=2))
Lesson 8: Class¶
Classes provide a means of bundling data and functionality on the data together.
Define a class using the keyword class
with a name¶
- Name in camel case by convension
- Optionally parentheses (put in super classes the current class inherits from)
- Then a colon, and finally an indented code block
The __init__()
magic method¶
Oftentimes, we want our class to be general and can handle arbitrary input data. This can be achieved by the __init__()
magic method.
- Constructor to assign values to the data attributes of the instance of the class at initialization.
self
represents the instance of the class. Usingself
, we can access the attributes and methods of the class.
class TwoDimVector:
def __init__(self, x, y):
self.x = x
self.y = y
v = TwoDimVector(6, 8)
print(v.x)
print(v.y)
User-defined class methods¶
- get access to and modify class attributes
- perform calculations
import math
class TwoDimVector:
def __init__(self, x, y):
self.x = x
self.y = y
def get_magnitude(self):
mag = math.sqrt(self.x ** 2 + self.y ** 2)
return mag
def scale_by_factor(self, factor=1.0):
self.x *= factor
self.y *= factor
v = TwoDimVector(3, 4)
print("vector magnitude", v.get_magnitude())
v.scale_by_factor(2)
print("vector magnitude after scale", v.get_magnitude())
The property
decorator¶
- A
decorator
feature in Python wraps in a function, appends several functionalities to existing code and then returns it - Property decorator makes the use of getter and setters easier
import math
class TwoDimVector:
def __init__(self, x, y):
self.x = x
self.y = y
def scale_by_factor(self, factor=1.0):
self.x *= factor
self.y *= factor
@property
def magnitude(self):
m = math.sqrt(self.x ** 2 + self.y ** 2)
return m
v = TwoDimVector(3, 4)
v.magnitude
Type annotation¶
Type hints for function and variables greatly improves code readability. To set type hints for an argument, add colon ":" and the expected type after the name of the argument.
- The Python runtime does not enforce function and variable type annotations
- But they can be used by third party tools such as type checkers, IDEs, linters, etc.
- The
typing
module provides more types, likeList
andDict
import math
class TwoDimVector:
"""
A two-dimensional vector class.
Args:
x: first component of a two-dimensional vector.
y: second component of a two-dimensional vedtor.
"""
def __init__(self, x: float, y: float):
self.x = x
self.y = y
def scale_by_factor(self, factor: float = 1.0):
self.x *= factor
self.y *= factor
@property
def magnitude(self) -> float:
m = math.sqrt(self.x ** 2 + self.y ** 2)
return m