Exit slides Esc

Function Factory

Slide 1 of 35

  1. Imagine making a greeting card for everyone in your group. Copy-pasting one card over and over is slow, and one change means many repairs.

    Today you build a card factory: define a machine once, then call it whenever you need it. Each idea comes as a short ladder — simplest first, then one step up — with a Notice naming the one new thing. At the end of each lesson you apply the same factory idea to turtle stamps.

    • Define a function with def and run it by calling its name.

    • Pass values into a function through parameters.

    • Hand a result back with return instead of only printing it.

    • Tell a local name from a global name (scope).

  2. Lesson 1: Build a Card Factory

    A function is a named block of code you define once with def and run later by calling its name. Defining it does not run it — a call does.

  3. def blank_card():
        print("+----------------------+")
        print("|                      |")
        print("+----------------------+")
    
    
    blank_card()

    Output

    +----------------------+
    |                      |
    +----------------------+
    

    def blank_card(): defines the machine; the last line blank_card() calls it, and only the call prints a card.

  4. def blank_card():
        print("+----------------------+")
        print("|                      |")
        print("+----------------------+")
    
    
    blank_card()
    blank_card()
    blank_card()

    Output

    +----------------------+
    |                      |
    +----------------------+
    +----------------------+
    |                      |
    +----------------------+
    +----------------------+
    |                      |
    +----------------------+
    

    Define once, call many — three calls reuse the same body with no copy-paste.

  5. A parameter is a blank the function fills in when it is called. greeting_card(name) has one parameter, name, that can hold a different value on every call.

  6. def greeting_card(name):
        print("+----------------------+")
        print(f"Happy coding, {name}!")
        print("+----------------------+")
    
    
    greeting_card("Maya")
    greeting_card("Leo")

    Output

    +----------------------+
    Happy coding, Maya!
    +----------------------+
    +----------------------+
    Happy coding, Leo!
    +----------------------+
    

    The parameter name is a blank — each call ("Maya", "Leo") fills it with a different value.

  7. def greeting_card(name, message):
        print("+----------------------+")
        print(f"{message}, {name}!")
        print("+----------------------+")
    
    
    greeting_card("Ada", "Well done")
    greeting_card("Sam", "Happy birthday")

    Output

    +----------------------+
    Well done, Ada!
    +----------------------+
    +----------------------+
    Happy birthday, Sam!
    +----------------------+
    
  8. Try it
    def greeting_card(name, message):
        print("+----------------------+")
        print(f"{message}, {name}!")
        print("+----------------------+")
    
    
    first_name = input("First name? ")
    first_message = input("First message? ")
    second_name = input("Second name? ")
    second_message = input("Second message? ")
    greeting_card(first_name, first_message)
    greeting_card(second_name, second_message)

    (It reads live input, so it does not run here.)

  9. One step up — two parameters, name and message, each a separate blank the call fills in order.

  10. Put it together: the same factory idea draws turtle stamps. Below is a simplified version of the idea in assets/l1_cards.py (turtle drawings run in their own window):

    def stamp(size):
        for edge in range(4):
            turtle.forward(size)
            turtle.right(90)
    
    for side in range(4):
        size = 18 + side * 4
        stamp(size)
        turtle.right(90)
  11. Here stamp is a USER-DEFINED function built from forward/right — one call draws one square stamp. Run the full drawing, assets/l1_cards.py, the same way as the Unit 3 turtle programs: from a terminal in this unit folder (py on Windows, python3 on a Mac), or with Thonny's Run button.

  12. Program
    # Bring in Python's turtle drawing tools.
    import turtle
    
    
    def stamp(size):
        """Draw one closed square stamp and finish where it began."""
        for edge in range(4):
            turtle.forward(size)
            turtle.right(90)
    
    
    # Set up the pen for a greeting-card design made from strokes.
    turtle.color("teal")
    turtle.pensize(3)
    
    # Draw a closed card border. No text command is needed.
    for edge in range(2):
        turtle.forward(220)
        turtle.right(90)
        turtle.forward(140)
        turtle.right(90)
    
    # Move from the card's top-left corner to a starting spot inside the card.
    turtle.penup()
    turtle.right(90)
    turtle.forward(25)
    turtle.left(90)
    turtle.pendown()
    
    # Make four pen-stroke stamps inside the card.
    # The loop counter changes every stamp's size and horizontal starting position.
    sides = 4
    for side in range(sides):
        size = 18 + side * 4
        position = side * 45
    
        turtle.penup()
        turtle.forward(position)
        turtle.pendown()
        stamp(size)
        turtle.penup()
        turtle.backward(position)
        turtle.pendown()
    
    # Return from the inside starting spot to the card's top-left corner.
    turtle.penup()
    turtle.left(90)
    turtle.forward(25)
    turtle.right(90)
    turtle.pendown()
    
    # Keep the drawing window open until you close it.
    turtle.done()
  13. Lesson 2: Hand a Value Back with return

    Some machines show their work; others hand a result back so the rest of the program can use it. return sends a value back to the line that called the function.

  14. def area(w, h):
        return w * h
    
    
    card_area = area(7, 4.5)
    message = f"The card area is {card_area} square units."
    print(message)

    Output

    The card area is 31.5 square units.
    
  15. Try it
    def area(w, h):
        return w * h
    
    
    w = float(input("Width? "))
    h = float(input("Height? "))
    card_area = area(w, h)
    message = f"The card area is {card_area} square units."
    print(message)

    (It reads live input, so it does not run here.)

  16. area does not print — it returns w * h, so card_area can store the result and the f-string can use it.

    def polygon_points(n):
        return 360 / n
    
    
    square_turn = polygon_points(4)
    seven_side_turn = polygon_points(7)
    print(f"A square turns {square_turn} degrees.")
    print(f"A seven-sided shape turns {seven_side_turn} degrees.")

    Output

    A square turns 90.0 degrees.
    A seven-sided shape turns 51.42857142857143 degrees.
    

    A returned value can be computed — 360 / n is a float, and the returned number flows straight into the f-string.

  17. Printing only DISPLAYS something; returning HANDS a value back for another job. That is why polygon_points can feed a turtle loop instead of repeating the division.

  18. Put it together: the shape factory uses the returned angle. Read this excerpt from assets/l2_shapes.py:

    def polygon_points(n):
        return 360 / n
    
    def polygon(n, side_length):
        turn_angle = polygon_points(n)
        for side in range(n):
            turtle.forward(side_length)
            turtle.right(turn_angle)

    Run the full shape factory, assets/l2_shapes.py, the same way, and find the three calls that build three different shapes.

  19. Program
    # Bring in Python's turtle drawing tools.
    import turtle
    
    
    def polygon_points(n):
        """Return the turn angle for a regular polygon with n sides."""
        return 360 / n
    
    
    def polygon(n, side_length):
        """Draw one closed regular polygon and finish where it began."""
        turn_angle = polygon_points(n)
        for side in range(n):
            turtle.forward(side_length)
            turtle.right(turn_angle)
    
    
    turtle.color("purple")
    turtle.pensize(3)
    
    # Each function call uses the returned float as its turn angle.
    polygon(3, 90)
    turtle.right(120)
    polygon(4, 70)
    turtle.right(120)
    polygon(5, 55)
    turtle.right(120)
    
    # Keep the drawing window open until you close it.
    turtle.done()
  20. Lesson 3: Keep Track of Scope

    A name made INSIDE a function is local to that function. A name made outside functions is global. Scope is the part of a program where a name is available.

  21. factory_name = "Function Factory"
    
    
    def pack_card(name):
        local_label = f"Card for {name}"
        print(factory_name)
        return local_label
    
    
    first_card = pack_card("Ari")
    print(f"Packed: {first_card}")

    Output

    Function Factory
    Packed: Card for Ari
    
  22. Try it
    factory_name = "Function Factory"
    
    
    def pack_card(name):
        local_label = f"Card for {name}"
        print(factory_name)
        return local_label
    
    
    name = input("Name? ")
    first_card = pack_card(name)
    print(f"Packed: {first_card}")

    (It reads live input, so it does not run here.)

  23. Inside pack_card, local_label is local (made inside) while factory_name is global (made outside) — the function can read the global and return its own local.

  24. Now meet a deliberate bug. Study the next cell, but do not run it — predict the last line of the traceback first.

    def make_label(name):
        card_label = f"Made for {name}"
        print(card_label)
    
    
    make_label("Nia")
    print(card_label)
    NameError: name 'card_label' is not defined
  25. card_label lives only INSIDE make_label; the final print runs outside that local scope, so the name is gone. The repair is to return the label and store it outside, exactly as Lesson 2 returned an area.

  26. Put it together: the final turtle factory nests loops, and each stamp(size) call keeps its own local edge counter. Below is a simplified version of the idea in assets/l3_stamps.py:

    def stamp(size):
        for edge in range(4):
            turtle.forward(size)
            turtle.right(90)
    
    def stamp_gallery(rings, stamps_per_ring):
        for ring_number in range(rings):
            size = 18 + ring_number * 5
            for stamp_number in range(stamps_per_ring):
                stamp(size)
                turtle.right(360 / stamps_per_ring)

    Each stamp call finishes its own square before the next begins. Run the full gallery, assets/l3_stamps.py, the same way.

  27. Functions let the outer design say what to build while the function body remembers how.

  28. Program
    # Bring in Python's turtle drawing tools.
    import turtle
    
    
    def stamp(size):
        """Draw one closed square stamp and finish where it began."""
        for edge in range(4):
            turtle.forward(size)
            turtle.right(90)
    
    
    def stamp_gallery(rings, stamps_per_ring):
        """Draw nested rings of stamps and restore the starting state."""
        for ring_number in range(rings):
            radius = 45 + ring_number * 25
            size = 18 + ring_number * 5
    
            for stamp_number in range(stamps_per_ring):
                turtle.penup()
                turtle.forward(radius)
                turtle.pendown()
                stamp(size)
                turtle.penup()
                turtle.backward(radius)
                turtle.pendown()
                turtle.right(360 / stamps_per_ring)
    
            turtle.right(360 / rings)
    
    
    turtle.color("darkgreen")
    turtle.pensize(2)
    
    # This first self-contained call marks the drawing's start position.
    stamp(24)
    
    # The gallery function contains an outer loop and an inner loop.
    # Every stamp call returns to its own starting position and direction.
    stamp_gallery(3, 6)
    
    # Keep the drawing window open until you close it.
    turtle.done()
  29. Algorithm Extension

    Extra loop practice — optional enrichment: these drills reuse the same loop shapes on small fixed data, so try as many as you like.

  30. A function can keep a running total

    Sometimes a factory needs to combine several values before it returns. Start total at 0, update it once for each value, and return the finished total.

  31. def total_stamp_size(number_of_stamps):
        total = 0
        for stamp_number in range(number_of_stamps):
            size = 10 + stamp_number * 5
            total = total + size
        return total
    
    
    three_stamp_total = total_stamp_size(3)
    print(three_stamp_total)

    Output

    45
    
  32. Try it
    def total_stamp_size(number_of_stamps):
        total = 0
        for stamp_number in range(number_of_stamps):
            size = 10 + stamp_number * 5
            total = total + size
        return total
    
    
    number_of_stamps = int(input("How many stamps? "))
    three_stamp_total = total_stamp_size(number_of_stamps)
    print(three_stamp_total)

    (It reads live input, so it does not run here.)

  33. total is an accumulator. It begins at 0, and total = total + size adds one stamp size on every loop turn. The function returns the final total after the loop.

    • Defining a function does not run it; each call runs its body.

    • A parameter is a blank that each call fills in, in order.

    • return hands a value back, so the caller can save it and use it again.

    • A name made inside a function is local and is gone after the call; return it to keep it.

    You can now build a factory of cards and stamps from functions you call again and again.