print(round(2 * 4.50, 2)) # 2 wraps
print(round(1 * 12.00, 2)) # 1 bowl
print(round(5 * 3.20, 2)) # 5 fries9.0
12.0
16.0
Programming with Python
On to Episode 3.
. . .
Tobi has been “reusing” code the only way he knows how: he pasted the same receipt block 14 times, once per menu item. A ten-cent price change yesterday cost him a whole afternoon of hunting down copies.
. . .
Today the code learns to reuse itself. We meet the function, and build a first class.
Tobi totals each receipt line by hand, the same shape, over and over:
print(round(2 * 4.50, 2)) # 2 wraps
print(round(1 * 12.00, 2)) # 1 bowl
print(round(5 * 3.20, 2)) # 5 fries9.0
12.0
16.0
. . .
Same calculation, retyped every time. Change the rule once and you must fix it everywhere. There is a better way.
def: name the calculation onceWrite the calculation once, give it a name, and call it whenever you need it:
def line_total(qty, price): # def NAME(inputs):
return round(qty * price, 2) # indented body, hands a value back
print(line_total(2, 4.50))9.0
. . .
def starts the definition · the name is line_total · the block is indented · return hands the answer back.
qty, price2, 4.50def line_total(qty, price): # qty and price are PARAMETERS
return round(qty * price, 2)
print(line_total(3, 3.20)) # 3 and 3.20 are ARGUMENTS9.6
. . .
Same function, different arguments, different result. No retyping.
Tobi wants a 40 % tip on a 10 EUR bill… no wait, a 10 % tip on 40 EUR. He types it in a hurry. What prints?
def with_tip(bill, percent):
return round(bill * (1 + percent / 100), 2)
print(with_tip(10, 40))a) 44.0 b) 14.0 c) Error
. . .
Predict first. Pick a letter, then I reveal the answer.
b) 14.0: arguments land on parameters by position. 10 becomes bill, 40 becomes percent: a 40 % tip on 10 EUR. Python cannot read what Tobi meant, only where he put it.
def with_tip(bill, percent):
return round(bill * (1 + percent / 100), 2)
print(with_tip(10, 40)) # bill=10, percent=40
print(with_tip(40, 10)) # bill=40, percent=1014.0
44.0
return: hand the value backreturn sends a value back to whoever called the function, so you can store it and use it later:
def line_total(qty, price):
return round(qty * price, 2)
subtotal = line_total(3, 3.20) # catch what came back
print(subtotal)
print(subtotal + 1.50) # ...and keep using it9.6
11.1
label_price prints but has no return. What does the last line show?
def label_price(price):
print(f"{price:.2f} EUR")
result = label_price(8.50)
print(result)a) 8.50 EUR b) Error c) None
. . .
Predict first. Pick a letter, then I reveal the answer.
print shows, return hands backc) None: label_price prints its line while running, but with no return it hands back None. That None is what lands in result. Printing is not returning.
def label_price(price):
print(f"{price:.2f} EUR")
result = label_price(8.50) # prints while running...
print(result) # ...but the value handed back is None8.50 EUR
None
A parameter is the function’s own private copy. What happens inside stays inside:
def bump(n):
n = n + 10 # changes the function's OWN copy of n
return n
print(bump(3)) # 13, the returned value13
. . .
Names created inside a function live in its scope. They vanish when the function ends.
bump adds ten to its parameter. We call it, then print stock. What does the last line print?
def bump(n):
n = n + 10
return n
stock = 3
bump(stock)
print(stock)a) 3 b) 13 c) Error
. . .
Predict first. Pick a letter, then I reveal the answer.
a) 3: bump changes only its own copy n. We never stored what it returned, so stock out here never moves. The function cannot reach out and rewrite your variables.
def bump(n):
n = n + 10
return n
stock = 3
bump(stock) # the return value is thrown away
print(stock) # still 33
stock = bump(stock)). Deliberate, visible. . .
Isolation is what makes functions safe to reuse. That is the whole point of this episode.
Sometimes data and the things you do with it belong together. A class is a blueprint that bundles both:
. . .
A method is just a function that lives inside a class and can read the object’s own data.
class, __init__, and selfclass Delivery:
def __init__(self, courier, distance_km): # runs when you build one
self.courier = courier # store data ON the object
self.distance_km = distance_km
self.rate_per_km = 1.20 # every delivery carries its rate
def fee(self): # a method: note self
return round(self.distance_km * self.rate_per_km, 2). . .
__init__ sets up a new object · self is this object · attributes are stored on self and read back through self. fee() multiplies two stored attributes.
Instantiate the class to make an object, then use its data and its methods:
trip = Delivery("Nadia", 4) # build one, __init__ runs
print(trip.courier) # read stored data
print(trip.fee()) # call the methodNadia
4.8
. . .
trip is one Delivery object. trip.fee() computes from its own stored distance: data and behavior, traveling together.
Two deliveries get built from the same class, one after the other. What does the last line print?
trip = Delivery("Nadia", 4)
second = Delivery("Omar", 10)
print(trip.fee())a) 12.0 b) Error c) 4.8
. . .
Predict first. Pick a letter, then I reveal the answer.
c) 4.8: __init__ ran twice and stored the data on two different objects. self inside trip.fee() is trip, so it reads Nadia’s 4 km, not Omar’s 10. Instances do not share attributes.
trip = Delivery("Nadia", 4)
second = Delivery("Omar", 10)
print(trip.fee()) # self is trip: 4 km
print(second.fee()) # self is second: 10 km4.8
12.0
__init__ and one method. That’s itself confusion), and that is not this course. . .
If the self keyword feels odd right now, copy the shape from the worked example. It gets clearer after you have written a few.
return, give a parameter a default, and build the startup’s first Order class. . .
Download your .py before you leave. Closing the tab without downloading loses your work, and downloading is exactly how you just handed in the checkpoint.
def, takes parameters, and hands a value back with return: print shows, return gives back (no return → None)self, via __init__) with behavior (methods). You’ll write just one small one today. . .
Next time, Episode 4: the menu outgrows Tobi’s seventeen loose variables (price1, price2, price_final_FINAL2), and nobody can find anything. We give the data a shape: lists and dictionaries.
. . .
Nothing new here, but these are still great books to start with!
. . .
For more, see the literature list of this course.