Pop an object from a class list

Question:

I have 2 classes. AlchemicalStorage class is used to store the AlchemicalElement objects.

class AlchemicalElement:

    def __init__(self, name: str):
        self.name = name

    def __repr__(self):
        return f'<AE: {self.name}>'


class AlchemicalStorage:

    def __init__(self):
        self.storage_list = []

I already have a function that adds elements to AlchemicalStorage. What I need is a function that removes and returns previously added element from the storage by its name.

What I have so far:

def add(self, element: AlchemicalElement):
    if isinstance(element, AlchemicalElement):
        self.storage_list.append(element)
    else:
        raise TypeError()

def pop(self, element_name: str) -> AlchemicalElement or None:
    """
    Remove and return previously added element from storage by its name.

    If there are multiple elements with the same name, remove only the one that was added most recently to the
    storage. If there are no elements with the given name, do not remove anything and return None.

    :param element_name: Name of the element to remove.
    :return: The removed AlchemicalElement object or None.
    """
    self.storage_list.append(element_name)
    return self.storage_list.pop()

Obviously pop() function is incorrect. I can’t figure out what the logic is here.

An example of what I want to achieve:

storage.add(AlchemicalElement('Fire'))
storage.add(AlchemicalElement('Water'))
storage.add(AlchemicalElement('Water'))
print(storage.pop('Water') == element_three)  # True
print(storage.pop('Water') == element_two)  # True
Asked By: fallguy

||

Answers:

You can just iterate through your storage list backwards and return the first instance, if it exists:

def pop(self, element_name: str) -> AlchemicalElement | None:
    for element in reversed(self.storage_list):
        if element.name == element_name:
            self.storage_list.remove(element)
            return element
    return None
Answered By: jprebys

If you define the __eq__ (equality) function for AlchemicalElement, you can check if the element is in a list using in. See the Python documentation about eq. If you want to compare to strings ('Helium') as well as to Elements, you should use isinstance() to check the type of other and act accordingly.

Now, in AlchemicalStorage, you can check if an element is in the list, using in. If it is in the list, you can reverse() the list, use remove( (which removes the first occurrence, but because you reversed it, you remove the last) and then reverse() it again.

The code below illustrates this.

I used typing hints; you may ignore that if you’re not familiar with that.

from typing import Union

class Element:
	def __init__(self, name: str):
		self.name = name

	def __eq__(self, other):
		return self.name == other.name

	def __repr__(self) -> str:
		return self.name


class Element_List:
	def __init__(self):
		self.elements = []

	def add(self, element: Element) -> None:
		self.elements.append(element)

	def pop(self, element: Element) -> Union[None, Element]:
		if element in self.elements:
			self.elements.reverse()
			self.elements.remove(element)
			self.elements.reverse()
			return element
		return None


# Wel will try to remove the last Helium element from the list...
elements = [Element('Hydrogen'), Element('Helium'), Element('Lithium'), Element('Helium'), Element('Lithium')]
element_list = Element_List()
for element in elements:
	element_list.add(element)

print(f'Element list before pop: {element_list.elements}')
element_list.pop(Element('Helium'))
print(f'Element list after pop: {element_list.elements}')

Try it online!

Answered By: agtoever
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.