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. Pick a letter, then I reveal the answer.
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
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.
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. Pick a letter, then I reveal the answer.
print shows, return givesdef 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
c) 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.
A parameter is the function’s own name for the value it was handed. Giving that name a new value inside changes nothing outside:
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.
stock move?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. Pick a letter, then I reveal the answer.
def bump(n):
n = n + 10
return n
stock = 3
bump(stock) # the return value is thrown away
print(stock) # still 33
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.
stock = bump(stock)). Deliberate, visibleIsolation 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. Pick a letter, then I reveal the answer.
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
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. Two objects of one class do not share their data.
__init__ and one method. That’s itself confusion), and that is not this courseIf 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 classDownload 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 todayNext 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.