Stacking Functions

Jed Rembold

February 13, 2023

Announcements

  • I’m through all of the Karel problem from PS2 feedback, and so things are going faster now. Will be completely finished by tomorrow. Sorry for the delay.
  • Have you started Wordle? If not, get started today!
    • 5 days for 5 milestones will give you plenty of time to figure things out and have some time for an extension if you want
  • Yet again, my availability today is almost non-existent, but I’ll be around for 30 min after class or after 5pm
  • Another Visiting Faculty Candidate today!
    • Teaching Demonstration at noon (Smullin B17)
    • Research Talk at 1 (Smullin B17)
      • Using AI to write songs!
  • Polling: rembold-class.ddns.net

Review Question

When the final line of the code to the right is run, what type of variable is x?

  1. integer
  2. float
  3. string
  4. None of the above
def func(A):
    m = str(A)
    n = m + m + m
    print(n)

y = 5.0
x = func(y)
print(type(x))

Parameter Purposes

  • Often functions need some sort of outside input in order to be useful
  • It is necessary for them to know enough details so that they can carry out the requested task, but not so many that the function becomes annoying to use
    • Imagine you were trying to accomplish the task yourself!
    • What is the minimum amount of information you would need to know?
  • The minimum necessary information needed for the function to accomplish its task is generally the information conveyed in the parameters
  • There is always a balance
    • More parameters makes your function more general, to be used elsewhere
    • More parameters are tedious and potentially error-prone to enter if unnecessary

Jockeying for Position

  • So far we have used a positional way to assign arguments to parameters

    >>> def func( first, second, third ):
            print( first, second, third )
    >>> func(1,2,3)
    1 2 3
    >>> func(2,6,4)
    2 6 4
    • First argument to first parameter, second to second parameter, etc

The Word is Key

  • Arguments may also be specified by a keyword, in which the caller precedes the argument with a parameter name and equals sign

  • Always stores the argument value in the specified parameter

    >>>  def func( first, second, third ):
          print( first, second, third )
    >>>  func(third=4, first=2, second=6)
    2 6 4
  • Keyword arguments can appear in any order

  • All keyword arguments must come after any positional arguments!

Default Slide Title

  • Python allows you to specify a default value for a parameter, which is will use if one is not directly supplied

  • Do so by adding an equals sign and a value after the parameter name

    def introduction(name='Jed', age=37):
      print('My name is ', name, ' and I am ', age)
    • So you define default values when you define the function
  • If, when calling a function, you are providing any further arguments after a default parameter, you must indicate them through keywords

Default Example

>>> introduction()
My name is Jed and I am 37
>>> introduction('Bob', 25)
My name is Bob and I am 25
>>> introduction('Larry')
My name is Larry and I am 37
>>> introduction(age=68)
My name is Jed and I am 68

Returning Graphics

  • You can return any type of variable from a function, including GObject graphical objects

  • Can be useful to write simple functions that bundle together common tasks

  • For instance, to create a filled circle centered at some location:

    def make_filled_circ(x_cent, y_cent, radius, color='black'):
      circle = GOval( x_cent-radius, y_cent-radius, 
                      2*radius, 2*radius)
      circle.set_color(color)
      circle.set_filled(True)
      return circle

Quite the Reader

  • You can return any type of variable from a function, including GObject graphical objects
  • Can be useful to write simple functions that bundle together common tasks
  • Can create our own Python library by constructing a collection of constants or functions
  • All code not in functions gets executed as soon as we import so generally don’t want extraneous print statements or to be running any code directly
  • Want the library to be in the same location as any code we want to import the library in

PGL Help Library

from pgl import GRect, GLabel
import random

def create_filled_rect(
    x_cent, y_cent, width, height, fill_col='black', border_col=None
):
    """
    Creates a GRect object with the desired fill color. 
    If a border color is specified, also draws the 
    border in the desired color.
    """
    rect = GRect(x_cent-width/2, y_cent-height/2, width, height)
    rect.set_filled(True)
    if border_col is None:
        rect.set_color(fill_col)
    else:
        rect.set_color(border_col)
        rect.set_fill_color(fill_col)
    return rect

def random_color():
    """
    Returns a random opaque color as a hex string.
    """
    color = "#"
    for i in range(6):
        color += random.choice("0123456789ABCDEF")
    return color

def create_centered_label(x_cent, y_cent, text, font=None):
    """
    Creates a GLabel object and centers it on the coordinates
    x_cent and y_cent.
    """
    label = GLabel(text)
    if font is not None:
        label.set_font(font)
    label.set_location(x_cent - label.get_width() / 2, 
                       y_cent + label.get_ascent() / 2 )
    return label

Poll!

def Vegas(x):
    y = 2
    for i in range(5):
        x += y
    return x

x = 3
z = Vegas(x)
print('z =', z)
print('x =', x)

Consider the code to the left. When the final value of x is printed, what will its value be?

  1. 3
  2. 5
  3. 13
  4. None

Stacks and Scopes

  • Functions really do work as self-contained little boxes or environments!
    • “What happens in Vegas stays in Vegas
  • Whenever Python enters a new function, you can envision that it gets out a fresh new board to keep track of that function’s variables
    • What happens on that board does NOT affect whatever might have been defined or happened on other boards
    • Each board is commonly called a stack frame
  • When Python finishes with a function and returns, the board or stack frame for that function is thrown away!

Taking a Walk through Vegas

We’ll annotate the stack frames by hand as the earlier code runs:

def Vegas(x):
    y = 2
    for i in range(5):
        x += y
    return x

x = 3
z = Vegas(x)
print('z =', z)
print('x =', x)

Summary of a Function Call

  1. Evaluate the arguments in the context of the caller
  2. Reserve space for the function in a new stack frame
  3. Copy each positional argument to the corresponding parameter variable
  4. Copy each keyword argument to the parameter with that name
  5. For parameters with default values, if not already assigned, assign those values
  6. Evaluate statements in the function body, using current stack frame to look up values of local variables
  7. On encountering a return, compute the return value and substitute that value in place of the function call
  8. Remove the stack frame
  9. Return to the caller, continuing from just after the function call

Understanding Check

Riddle me this. What would be the printed value of z at the end of the code to the right?

  1. 27
  2. 25
  3. 19
  4. None of the above
def f(x,y):
    z = (x + 3) ** 2
    return y + z

x = 1
z = x + f(y=x,x=2)
print(z)

Name Resolution and Scope

  • When Python encounters a variable name in a program, it looks for where the variable was defined in an expanding search:
    1. Local - The local context is all the variables defined within the current function. This includes variables appearing as a parameter!
    2. Enclosing - The enclosing context consists of the names defined in a function enclosing the current one.
    3. Global - The global context consists of names defined outside of any function or imported into the current module.
    4. Built-in - The last place Python looks is in the names of any built-in functions, like abs, str, print, etc.
  • The part of a program in which a name is defined in called its scope

Scoping Example

def func1(x,y):
    return z + func2(x,y)

def func2(x,y):
    def func3(x):
        return (y + x) ** 2

    z = x - func3(y)
    return z - y


z = 1
print(func1(2,z))

Local Variables

  • In Python, assigning any value to a variable means that the variable is assumed to be local

    • This generally makes sense, as you would not want more specific functions overriding variables in other areas
  • Can lead to issues though:

    def increment():
        x = x + 1
    
    x = 0
    increment()
  • There are a few ways to address this, but we’ll focus on one in particular when it comes to PGL

// reveal.js plugins