Inside the memory of the computer, the programming language sets aside two areas to store info.
Our first concept, that we're familiar with, is the 'known names' area (traditionally known as the Environment).
We might visualize it as:
rate => 10
name => "Kathi"
When we think about storing data, we will now introduce a new area for data storage called the ~heap.
The heap can be thought of as labelled slots in memory. This is used in conjuction with the Environment.
Given an Environment with the following:
noon = time(12,0)
midnight = time(0,0)
The heap might look something like:
label | slot |
---|---|
1000 | time(12,0) |
1001 | time(0,0) |
1002 | slot |
With lists, this becomes a little more obfuscated:
Given an Environment with the following:
appts = link(time(9,30),
link(time(10,45),
link(time(14,0),
empty)))
The heap might look something like:
label | slot |
---|---|
1000 | time(12,0) |
1001 | time(0,0) |
1002 | time(9,30) |
1003 | time(10,45) |
1004 | time(14,0) |
1005 | link(loc 1004,empty) |
1006 | link(loc 1003,loc 1005) |
1007 | link(loc 1002,loc 1006) |
Now that we've gotten this concept out of the way, let's start to migrate away from Pyret.
It's time to move into Python!
To start learning Python, we will translate the pen-cost program we worked through in Pyret.
In order to define a function, we will use the command def
. When defining the parameters of the function, you should note some important differences.
my_function()
.myVar
.# start by translating the pen-cost program
# if you don't have an integer value, you should use float type
def pen_cost(numPens: int, message: str) -> float:
"""total cost for pens at 25 cents each plus 2 cents per character"""
return numPens * (0.25 + (len(message) * 0.02)) #use return to print to Python Console
# now, let's try our new function. this input should return 2.9
pen_cost(10, "hi")
Notice that we didn't have to use end
to indicate the end of a function block. The community that developed Python was more concerned with doing things quickly (wanted to get rid of extra things like end
). They used indentation to help Python know when a "new" thing starts. This has some funny implications we will be covering later.
Python interprets the indentation of lines of code as signifying the beginning and end of things. This indentation can be achieved by either tabs or 4 spaces. Pick one and stick with it. You must be consistent throughout your code.
# this will not work as we have not imported testlight.py
# the below code is purely for demonstration
def test_pens():
test("3 pens, 3 chars", pen_cost(3, "wow"), 0.93)
test("10 pens, 5 chars", pen_cost(10, "smile"), 3.5)
This was done
def pepper_scale(rating: int) -> str:
"""given a scoville rating, return the name of the pepper"""
if (rating == 0):
return "bell pepper"
elif ((rating >= 100) and (rating <= 1000)):
return "paprika"
elif ((rating >= 3500) and (rating <= 10000)):
return "jalapeno"
else:
return "unknown"
pepper_scale(0) # should return "bell pepper"
pepper_scale(250) #should return "paprika"
pepper_scale(60000000) #should return "unknown"
print
and return
?¶def printAndReturnNothing():
x = "hello"
print(x)
def printAndReturn():
x = "hello"
print(x)
return x
def main():
ret = printAndReturn()
other = printAndReturnNothing()
print("ret is: %s" % ret)
print("other is: %s" % other)
main()
print
takes its arguments/expressions and dumps them to standard output, so in the functions I made up, print will output the value of x
, which is hello
.
printAndReturn
will return x to the caller of the method, so:
ret = printAndReturn()
ret
will have the same value as x
, i.e. "hello"
printAndReturnNothing
doesn't return anything, so:
other = printAndReturnNothing()
other
actually becomes None
because that is the default return from a Python function. Python functions always return something, but if no return is declared, the function will return None
.
In python, the []
operator indicates a list.
We can add to a list using the .append()
method. .append()
adds an element to the end of a list.
IMPORTANT: When we .append()
to a list, this mutates the list! It does not create a new instantiation of the list.
# imagine we want to make a list of the courses we're taking
# instantiate an empty list
taking = []
# append a course to the list
taking.append("CSCI50")
print(taking)
# append another course to the list
taking.append("VISA100")
print(taking)
When making a list, Python grabs a whole bunch of slots at once for the heap. We can illustrate this with our example above.
At first, when instantiating the list, it might look something like:
Environment (Known Names)
taking => loc 1015
Python then grabs 4 'slots', for example.
Python Heap
label | slot |
---|---|
1015 |
|
1016 |
|
1017 |
|
1018 |
|
Now, we can illustrate adding a course to our taking
list. After the taking.append("CSCI50")
call.
Environment (Known Names)
taking => loc 1015
Python Heap
label | slot |
---|---|
1015 | "CSCI50" |
1016 |
|
1017 |
|
1018 |
|
This is the reason why you can't ask for the rest
of a list (like you can in Pyret).
Let's work through some examples that illustrate how we could use Python for course registration.
from typing import List #only bringing this in to get type name for list
# register for a course by adding to a registration list
def register(regList: List[str], courseName: str) -> List:
"""consumes a list of registered courses and a new course to register
for, returns the list of registered courses"""
regList.append(courseName)
return regList
register(taking, "PHIL130")
But if we add PHIL130 again, we see a problem.
register(taking, "PHIL130")
It got added twice!
Let's write a function that fixes this issue by checking if something is a member of a list.
def registerImproved(regList: List[str], courseName: str) -> List:
"""consumes a list of registered courses and a new course to register
for, returns the list of registered courses"""
if courseName not in regList:
regList.append(courseName)
return regList
# instantiate a new taking list
taking2 = []
# append a course to the list
registerImproved(taking2, "CSCI50")
print(taking2)
# append a course to the list
registerImproved(taking2, "VISA100")
print(taking2)
# append the same course to the list
registerImproved(taking2, "VISA100")
print(taking2)
What if we wanted to raise an error to let the user know that they're trying to register for a course they already have in their registered course list?
We can use raise
!
# what if we wanted to raise an error if re-register?
def registerWithError(regList: List[str], courseName: str) -> List:
if courseName not in regList:
regList.append(courseName)
else: raise Exception("Already taking this course")
return regList
# instantiate a new taking list
taking2 = []
# append a course to the list
registerWithError(taking2, "CSCI50")
print(taking2)
# append a course to the list
registerWithError(taking2, "VISA100")
print(taking2)
# append the same course to the list
registerWithError(taking2, "VISA100")
Now, what if we wanted to drop a class? We can do this too. We will use the .remove()
function on our list.
# perhaps we decided that we didn't like art
# we can drop VISA100
# peek at the list
print(taking2)
taking2.remove("VISA100")
# peek at the list after removing
print(taking2)
We can use the in
operator to test membership. This can look a number of different ways.
# in can be used to test membership in a list
"CSCI50" in taking2
# in can also be used to test for substrings
"CS" in "CSCI50"
We can define a function that gets the names of registered courses from a given department in our "taking" list.
This serves as an introduction to anonymous functions (lambda functions) in Python.
lambda arguments: expression
Lambda functions can have any number of arguments but only one expression. The expression is evaluated and returned. Lambda functions can be used wherever function objects are required.
# get names of reg courses from a given department
def from_dept(regList: List[str], deptName: str) -> List[str]:
"""return names of all courses in list with give n deptName in them"""
return list(filter(lambda course: deptName in course, regList))
list()
constructor from filter?¶The filter function does not internally return a list. We need to use list()
to have our output converted to a list.
phone_charges
function from Quiz 1¶# how can we alter our code from quiz 1 such that it can work for both mins and texts?
BASE_RATE = 20
def phone_charges(numMinutes: int, numTexts: int) -> float:
"""compute phone plan charges based on minutes used and texts sent"""
# introduce a global variable total to be returned at end of function
total = BASE_RATE
if numMinutes > 120:
total = total + ((numMinutes - 120) * 0.05)
if numTexts > 10:
total = total + ((numTexts - 10) * 0.10)
return total