Download this file:
In this post, I’ll give you two sections of fill-in-the-blank questions. The first section tries to show you how functions are values. The second section is about how frames work.
At the bottom of this post, I explain the concepts and intuitions behind each answer and how I reach that result.
def square_root(number): return number ** 0.5 # Q1. # square_root(4) answers.append(___) catcus = square_root # Q2. # catcus(16) answers.append(___) # Q3. # square_root(16) answers.append(___) square_root = 25 # Q4. # square_root(9) answers.append(___) # Q5. # catcus(9) answers.append(___) # Q6. # catcus(square_root) answers.append(___)
Visualize how section 1 runs below:
pika = 10 # Q1. # What frame is pika in? answers2.append(___) def pokemon(pika): return pika * 2 # Q2. # pokemon(20) answers2.append(___) # Q3. # pokemon(pika) answers2.append(___) def foo(): return pika # Q4. # foo() answers2.append(___) def foo(): pika = 42 return pika # Q5. # foo() answers2.append(___) # Q6. # On Q5, when foo() was evaluated, what frame is the pika that it returned in? answers2.append(___) def bar(): apple = 10 # Q7. # bar() answers2.append(___) # Q8. # When bar() was evaluated in Q7, what frame was apple in? answers2.append(___) # Q9. # apple answers2.append(___)
Link to Python Tutor for section 2 below:
Explanation for each answer
Q1. The first question is pretty straightforward. Apply
square_root to the int 4 and you get 2. You can replace
square_root to 4 and 4 ** 0.5 is 2.
Q2 & Q3. The key here is to understand that now both
square_root points to the
catcus(16) is essentially the same as
square_root(16). At this moment, when you evaluate
catcus, it evaluates to the same function object as
Wondering about how to distinguish different function objects? We will talk about intrinsic names in the higher-order functions lesson. But a function object’s intrinsic name is basically what name it was given at first in your
Q4. When this question’s code is executed, you have to be aware that
square_root no longer points to the function object that it originally did:
square_root(9) would result in an error.
square_root is now an
int, and integers are not operators, so you can’t call them.
Q5. Remember that
catcus(I noticed the typo but am too lazy to change it) is still pointing to the original
square_root function object. So, to evaluate
catcus(9), the code in the original
square_root function is executed. The body of the original
square_root function just contains
return number ** 0.5, so you bind 9 to
number and what gets returned is 3.0.
Q6. In this question, just follow the 3 step rule to evaluating call expressions. Since
square_root is now bound to the
catcus(square_root) equals 5.0.
Q1. At this point,
pika is not in the body of any function nor is any function called. So, it is in the global frame:
Q2. Now, a
def statement had been executed. We have a
pokemon function object, which is also stored in the variable
pokemon in the global frame:
pokemon(20) is pretty straightforward. Bind 20 to the parameter
pokemon, and 40 is outputted.
Q3. Here comes the confusing part. When you evaluate the call expression
pokemon(pika), you are still in the global frame. So, following the variable search rule,
pika comes from the global frame, which is 10. Then, you would bind 10 to the
pika parameter inside
pokemon, and you can execute the body, which returns 10*2 = 20:
Q4. This question tests you on the variable search rule. Here, a
foo function had been created and it returns
foo() is evaluated, a new frame is opened first:
However, there are no parameters nor variables in this function. So, there aren’t any variables under this frame. What happens when it executes the return statement and needs to evaluate
pika then? Well, you go to the parent frame, which is the global frame. In the global frame, you do indeed find
pika, so you take that value. Now we found out
pika is 10, we return 10:
Q5. When you reach this question, the first
foo function defined is lost because we redefined it in line 91. So, when you evaluate
foo(), it’s no longer the same result as the previous question. In the body of this new
foo function, we did create a variable called
pika with value 42:
So, when executing the return statement, you search in the current frame for
pika first. Because you can find it in the current frame, you use that directly and return 42.
Q6. Since the
foo() in Q5 was our 4th call expression evaluated, it’s also the 4th frame we’ve opened. If you look at Python Tutor, it’s pretty obvious:
Q7. For this question, a new function named
bar had been defined. This question tests you on the return statement’s properties. Because the
bar function does not have any return statements, it returns
None by default. Remember to capitalize the ‘N’.
Q8. Because the call expression
bar() was evaluated, we needed to create a new frame for it. Next, when we executed the body of
bar, the first thing we did was assign the value 10 to
apple. Since this was done inside the
bar function, it was under that call frame, which was
Q9. This is also a question testing you on the variable search rule.
apple only existed in call frame
f5, and the variable search rule states that you cannot see or use any variables in child frames. When we try to evaluate
apple here, we are in the global frame because there aren’t any call expressions being evaluated nor are we in a function’s body. Since
f5 is the global frame’s child frame, we can’t access the variables inside. Thus, this results in an error.
You don’t need to submit anything for this.