FAQ: Stacks: Python - Stacks Python Push and Pop

So, when I first do these lessons I’m using VSCode to suss out the solution and this was the initial code I used.

def push(self, value):
    item = Node(value, self.top_item)
    self.top_item = item

When Codecademy generated the error I realize you wanted this:

def push(self, value):
    item = Node(value)
    item.set_next_node(self.top_item)
    self.top_item = item

And, from your explanation, I take it both work equally well at doing exactly what is being asked. However, if we wanted to include something like print(“Node added.”) later on, putting that into the .set_next_node() method is preferable to doing it in .push(), right?

Hello @coreblaster01537,

As you are developing the code for a data structure, use calls print where you need them for debugging purposes. After refinement is complete, remove the calls to print when they are no longer necessary for the proper functioning of the data structure.

For a general purpose data structure, such as a stack, you probably would not want to permanently include calls to print, because the output would likely become a liability. Therefore, after debugging is complete, and you are using the data structure to perform a task, use external calls to print to test and debug the code for performing that task.

Following is code that might be used for debugging a calculator that uses a stack:

# functions for debugging the calculator
def push_with_report(val, stack):
  stack.push(val)
  print(f"Pushed: {val} ")

def pop_with_report(stack):
  val = stack.pop()
  print(f"Popped: {val} ")
  return val

calc = Stack()
push_with_report(7, calc)
push_with_report(4, calc)
val = pop_with_report(calc)

Output:

Pushed: 7 
Pushed: 4 
Popped: 4

Edited on November 12, 2020 to add the following:

As a general principle, use print calls to first test the lower level components of a system, Then, as the lower level components are perfected, progressively move the use of print calls to higher level components that are being tested.

1 Like

Just wondering/noticing, the pop method doesn’t actually “remove” a node in this exercise, right?
It just returns data.

The code of the pop method:

  def pop(self):
    item_to_remove = self.top_item
    self.top_item = item_to_remove.get_next_node()
    return item_to_remove.get_value()

When we want to remove nodes, we don’t actually delete them in these exercises. Rather, say the node we want to remove is node_x. We assign the link of node_x's previous node to node_x's following node. Example follows.

Before Removal

nodes: node_w | node_x | node_y
links: node_x | node_y | node_z

After Removal

nodes: node_w | node_x | node_y
links: node_y | node_y | node_z

Since node_x is no longer accessible because there’s no way to navigate through it since node_w has reassigned its next_node, it is essentially “removed”.


We remove the link to item_to_remove in the pop() method, then return its value. So, in short, we do remove the node from the stack in addition to returning its value.

3 Likes

Oh yes! I forgot about how nodes link and how that effects things. Thank you for this insight!

1 Like

I’m a beginner here just like you. But from what i understand, when a node is pointing to another node say
Node_a points to Node_b
and you then point Node_c to Node_b, Node_a is orphaned and therefore removed. I believe why we sent the_head_node to None while traversing the linked list in the previous linked list segment was to exit the while loop. But please clarify to be sure. Hope this helps.

This is false. Nodes are orphaned when there is no way of accessing them because there are no other nodes pointing to them. I provided an example a bit earlier in this topic.

If I were to illustrate the situation you described, it would look like the following.

node_a  -->  node_b
node_c  -----^

As you can see, both node_a and node_c would point to node_b. Just because we made node_c point to node_b, that doesn’t mean that node_a no longer points to node_b.


That aside, the exercise the user is asking about is related to stacks, not linked lists, so the implementation and functionality are different. The Stack class in the exercise uses an instance variable to keep track of the top-most node and this is used in orphaning/removing the top value when we want to pop from the stack.

1 Like

Now I’m back in a state of total confusion and not knowing anything because that is how i have been explaining it to myself in my notes since the linked lists lesson. I need help please?!!

In a singly linked list, there is only one direction of traversal through the list.

node_a -> node_b -> node_c -> null

We would not link node_c to node_a, but, if we wanted to remove node_b, we would link node_a to node_c and the orphaned node_b would drop out of the picture. In a key ring, node_c would link forward to node_a, but only in that configuration.

Only in a doubly linked list would we point from node_b back to node_a. In a key ring we would be able to traverse in both directions given the correct links as apply.

node_a -> node_b -> node_c -> node_a
node_a <- node_b <- node_c <- node_a

In order for node_b to be orphaned, both links from node_a and node_c would need to be removed.

Not sure any of this helps with your question. See if you can find a way to frame the question around the most confusing bits and we can have another go at it.

1 Like

This is exactly what I thought was happening actually. Thank you very much.

1 Like

If I’m not misunderstanding, shouldn’t the representation be:
nodes: node_w becomes node_w.next_node = node_x | node_y
links: node_y | node_z
?
As we are not removing the first node with the .set_next_node() method, but with = get_next_node()?

Sorry, I’m not understanding your question. Could you rephrase it and format the example so it’s easier to see what you’re trying to convey?

I completely missed the fact that you gave a different example with node number 2 being removed, not the head node.
Sorry for wasting your time :frowning:

But since I have your attention, and to not have it be a complete waste of your time, shouldn’t it be:

We remove the link from item_to_remove in the pop() method, then return its value. So, in short, we do remove the node from the stack in addition to returning its value.
instead of:
“We remove the link to item_to_remove in the pop() method, then return its value. So, in short, we do remove the node from the stack in addition to returning its value.”

Or is there no direction connotation necessary when we talk about links?

No worries, we’re all here to help one another! :slight_smile:

I think using the word “link” might be confusing you a bit here. I believe that to should be used rather than from. When we remove the link to a node, we remove our ability to access that node. When we remove the link from a node (here’s where you might have gotten confused), if we are talking about a node’s .next_node as the link, then we are removing our ability to access another node and not the node we are currently looking at.


I’ll try explaining this another way. Because the Stack class uses a .top_item instance variable to keep track of the topmost node, rather than removing a node’s .next_node link to item_to_remove, we are changing the value of self.top_item and thereby removing our ability to access it in the stack.

What’s happening in the .pop() method is as follows:

  1. We set item_to_remove to the topmost node in the stack.
  2. We set the .top_item instance variable of the stack to have the value of self.top_item.get_next_node(). This means we are setting the topmost node of the stack to the node that is second to the top of the stack.
  3. Since self.top_item is equal to the second-topmost node in the stack, and we don’t have any other way of accessing the topmost node (which is stored in item_to_remove), the topmost node is inaccessible and considered orphaned. This is because our only way of accessing the topmost node was through the .top_item instance variable and now .top_item stores a different node.
  4. We return the popped node, item_to_remove.
1 Like

I appreciate the detailed answer, it helped to really sediment the notions in my head.
I see the distinction more clearly now.
What was causing my(more of a linguistical) confusion was the fact that when learning nodes and linked lists we saw that nodes contain data and a pointer “->”, a link to the next node(which implies a directional component: A = Node(value, B) the pointer is from A to B). A top.item has no other node linking to it, in this paradigm.
But in the example given, I guess by removing any link to the head node we mean “we remove any way to access our head node” and I see how saying we remove any link from the Node would not correctly express what is happening - us removing the node completely.

Thank you for the responses, they always help a lot!

1 Like