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
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)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 NoneWhen 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:
def push(stack, item):
stack.append(item)
s = []
push(s, 5)
push(s, 8)
print(s)[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.
Now write the push the tempting way, with a new list:
def push(stack, item):
stack = stack + [item]
s = []
push(s, 5)
push(s, 8)
print(s)[]
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.
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:
names = ['Ravi', 'Meera', 'Amit']
status = []
def Push_element():
for n in names:
status.append(n)
Push_element()
print(status)['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:
names = ['Ravi', 'Meera', 'Amit']
status = []
def Push_element():
for n in names:
status = status + [n]
Push_element()
print(status)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 valueBecause 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:
def pop(stack):
if len(stack) == 0:
print('Underflow')
else:
return stack.pop()
s = [10]
print(pop(s))
print(pop(s))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
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)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:
stack = [78, 91, 65]
while len(stack) > 0:
print(stack.pop())
print('after displaying:', stack)65 91 78 after displaying: []
6Try it
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
if isEmpty(stack): — or the program stops with IndexError.
The parameter and the caller's variable are one list, and append() changes it.
That builds a new list the caller never sees — and on a global list it raises UnboundLocalError.
print(pop([])) shows Underflow and then None.
range(len(stack) - 1, -1, -1), or stack[::-1].
A while loop of pop() empties the stack it was meant to show.
- 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
Change
display()so it prints the whole stack on one line, top first.Hint ·
print(stack[i], end=' '), then oneprint()after the loop. - 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.
def push(stack, item): stack.append(item). Why does s = []; push(s, 5); print(s) print [5] with no return?
pop() prints 'Underflow' when the stack is empty and otherwise returns stack.pop(). What does print(pop([])) show?
Which loop displays stack = [78, 91, 65] top first and leaves the stack unchanged?
status = [] is made at the top of a program. Inside a function, the line status = status + [n] runs. What happens?