LambdaLabTM
Computer Science · Class 12 · Data Structures
stack in Pythonfunctions⏱️ 15 min read

Writing the Stack Functions

Board questions never say “use append()”. They say “write a function push_book(BooksStack, new_book)”. So each stack operation gets wrapped in a small function with a stack word for a name. There are five of them, none longer than a few lines.

1The five functions

stack_functions.py
def isEmpty(stack):
    if len(stack) == 0:
        return True
    else:
        return False

def push(stack, item):
    stack.append(item)

def pop(stack):
    if isEmpty(stack):
        print('Underflow')
        return None
    else:
        return stack.pop()

def peek(stack):
    if isEmpty(stack):
        print('Stack is empty')
        return None
    else:
        return stack[-1]

def display(stack):
    if isEmpty(stack):
        print('Stack is empty')
    else:
        for i in range(len(stack) - 1, -1, -1):
            print(stack[i])

marks = []
push(marks, 78)
push(marks, 91)
push(marks, 65)
display(marks)
print('top    :', peek(marks))
print('popped :', pop(marks))
print('now    :', marks)
Output
65
91
78
top    : 65
popped : 65
now    : [78, 91]
def isEmpty(stack):

Written first because two of the others use it. It answers one question and changes nothing.

def push(stack, item):

One line inside, and no return. Section 2 explains why the caller still sees the new item.

if isEmpty(stack):

The underflow check, before pop() and before peek(). Without it, an empty stack stops the program with IndexError.

return None

When there is nothing to pop, the function prints its message and hands back None, so the caller can tell nothing came out.

range(len(stack) - 1, -1, -1)

Positions from the last one down to 0. That prints the top first, which is the order the stack would give the items back.

2Why push() needs no return

push() ends without a return, and yet the caller's list grows:

same_list.py
def push(stack, item):
    stack.append(item)

s = []
push(s, 5)
push(s, 8)
print(s)
Output
[5, 8]

When push(s, 5) runs, the parameter stack is not a copy of s. It is a second name for the same list. append() changes that one list, so the change is there when the function ends.

The lesson these programs practiseSharing & Copying — two names on one list

Now write the push the tempting way, with a new list:

rebind_trap.py
def push(stack, item):
    stack = stack + [item]

s = []
push(s, 5)
push(s, 8)
print(s)
Output
[]

Nothing was pushed. stack + [item] builds a brand-new list, and stack = moves only the function's own name onto it. The caller's list is never touched, and the new list is thrown away when the function ends. There is no error to warn you.

Change the list, never replace it
Inside a stack function, use append() and pop(). Those change the list the caller gave you. Assigning a new list to the parameter changes nothing the caller can see.

3When the stack is a global list

Many board questions do not pass the stack in at all. They name it — “a stack named status” — and ask for Push_element() with empty brackets. Then the list is made at the top of the program and the function uses it by name:

global_stack.py
names = ['Ravi', 'Meera', 'Amit']
status = []

def Push_element():
    for n in names:
        status.append(n)

Push_element()
print(status)
Output
['Ravi', 'Meera', 'Amit']

No global statement is needed. status.append(n) only reads the name status to find the list, then changes the list. It never assigns to the name. Assign to it, and the function breaks:

global_trap.py
names = ['Ravi', 'Meera', 'Amit']
status = []

def Push_element():
    for n in names:
        status = status + [n]

Push_element()
print(status)
Output
Traceback (most recent call last):
  File "global_trap.py", line 8, in <module>
    Push_element()
  File "global_trap.py", line 6, in Push_element
    status = status + [n]
             ^^^^^^
UnboundLocalError: cannot access local variable 'status' where it is not associated with a value

Because status = appears inside the function, Python treats status as a local name for the whole function — and the local one has no value yet when the right-hand side tries to read it. That is the UnboundLocalError from the scope lesson, and append() avoids it completely.

4Underflow: the message is printed, the None is returned

The board's wording is usually “pop the topmost item and return it; if the stack is empty, display Underflow”. Follow that exactly, and see what the caller gets:

underflow_none.py
def pop(stack):
    if len(stack) == 0:
        print('Underflow')
    else:
        return stack.pop()

s = [10]
print(pop(s))
print(pop(s))
Output
10
Underflow
None

The second call prints Underflow itself, then ends without returning anything — so it hands back None, and the print() around the call prints that. Nothing is wrong. The question asked for a message and a value, and those are two separate things. Writing return None yourself, as in section 1, just makes the second one visible.

5Displaying a stack: top first, and without popping

display_order.py
stack = [78, 91, 65]

print('for item in stack:')
for item in stack:
    print(item)

print('top first, by position:')
for i in range(len(stack) - 1, -1, -1):
    print(stack[i])

print('top first, with a slice:')
for item in stack[::-1]:
    print(item)
Output
for item in stack:
78
91
65
top first, by position:
65
91
78
top first, with a slice:
65
91
78

for item in stack walks the list from position 0, which is the bottom. The other two start from the top. Both are correct; the slice stack[::-1] makes a reversed copy and leaves the stack alone.

One more way looks right and is not:

display_that_pops.py
stack = [78, 91, 65]

while len(stack) > 0:
    print(stack.pop())

print('after displaying:', stack)
Output
65
91
78
after displaying: []
A display must not pop
The printout is perfect, and the stack is gone. When a question says “display without deleting”, it is ruling this out. Popping everything is right only when the question asks you to pop them all.

6Try it

stack_try.py

This version of isEmpty() is one line: len(stack) == 0 is already True or False, so it can be returned directly. Both versions do the same job.

7Recap

Check before pop and peek

if isEmpty(stack): — or the program stops with IndexError.

push() has no return

The parameter and the caller's variable are one list, and append() changes it.

Never stack = stack + [x]

That builds a new list the caller never sees — and on a global list it raises UnboundLocalError.

Message printed, None returned

print(pop([])) shows Underflow and then None.

Display top first

range(len(stack) - 1, -1, -1), or stack[::-1].

Display does not pop

A while loop of pop() empties the stack it was meant to show.

✍️ Now write these yourself
  1. 1

    Write size(stack), which returns how many items are on the stack.

    Hint · One line. It is len() with a stack word for a name.

  2. 2

    Change display() so it prints the whole stack on one line, top first.

    Hint · print(stack[i], end=' '), then one print() after the loop.

  3. 3

    Write pop_all(stack), which pops every item, prints each, and then prints Stack Empty.

    Hint · This one is allowed to pop — it is what the function is for.

Quick Check

def push(stack, item): stack.append(item). Why does s = []; push(s, 5); print(s) print [5] with no return?

Quick Check

pop() prints 'Underflow' when the stack is empty and otherwise returns stack.pop(). What does print(pop([])) show?

Quick Check

Which loop displays stack = [78, 91, 65] top first and leaves the stack unchanged?

Quick Check

status = [] is made at the top of a program. Inside a function, the line status = status + [n] runs. What happens?