Custom list class slicing functionality - python

I want to implement my own type of list called Stack. I also want to add slicing functionality with the __getitem__ method like so:
class Stack():
def __init__(self):
self.items = []
def __getitem__(self, slc):
return self.items[slc]
def append(self, item):
self.items.append(item)
Now if I create a Stack instance and append some elements to it I get the Stack type:
st = Stack()
st.append('hi')
st.append('bye')
st.append('hello')
print(type(st)) # Stack type
but if I slice my Stack, it becomes a list again:
st_sliced = st[1:2]
print(type(st_sliced)) # List type
how can I make st_sliced to stay Stack type after slicing?

return self.items[slc] return a list - this is why the type is not a Stack anymore.
The code below keep the type as Stack.
class Stack:
def __init__(self, items=None):
if items is None:
items = []
self.items = items
def __getitem__(self, slc):
return Stack(self.items[slc])
def append(self, item):
self.items.append(item)
st = Stack()
st.append('hi')
st.append('bye')
st.append('hello')
print(type(st))
st_sliced = st[1:2]
print(type(st_sliced))

Related

Object list fails to append

I'm trying to append some objects to a list but it fails with this error:
Traceback (most recent call last):
File "/home/miguelangel/PycharmProjects/reports/Output.py", line 23, in <module>
product.add_item(Item('product_id', 'product_id'))
File "/home/miguelangel/PycharmProjects/reports/Output.py", line 15, in add_item
self.items.append(item)
AttributeError: 'function' object has no attribute 'append'
I don't understand why it fails if a list is supposed to contain the append function. Here's the actual code.
class Item:
def __init__(self, column: str, alias: str):
self.column = column
self.alias = alias
class Category:
items = []
def __init__(self, category: str):
self.category = category
def add_item(self, item: Item):
self.items.append(item)
def items(self):
return self.items
product = Category('product')
product.add_item(Item('product_id', 'product_id'))
product.add_item(Item('product_name', 'product_name'))
print(product)
You make several mistakes here:
you use a class-attribute to hold items in a category. This is a mistake because there is only one of those available for all categories. So you would add all items to essentially a global list.
the root-cause of your problem is that you name the list items as well as the method. The latter one is shadowing the first, so when accessing self.items you end up with the method, that doesn't have an append method. Which is what your error tells you: a function object has no append.
Use a proper instance-variable items instead, and don't declare a needless accessor-method items. Languages like Java or C++ make you write accessors for reasons beyond the scope of this answer, but in Python one doesn't writer primitive getters and setters because attribute access is nicer, and properties are there to save the day if there would be the need for any code being run.
class Item:
def __init__(self, column: str, alias: str):
self.column = column
self.alias = alias
class Category:
def __init__(self, category: str):
self.category = category
self.items = []
def add_item(self, item: Item):
self.items.append(item)
product = Category('product')
product.add_item(Item('product_id', 'product_id'))
product.add_item(Item('product_name', 'product_name'))
Your getter for items clashes with the variable name. Change the variable name to _items or something else: it is good practice to put an underscore before private members anyways.
class Category:
_items = []
def __init__(self, category: str):
self.category = category
def add_item(self, item: Item):
self._items.append(item)
def items(self):
return self._items

Difference between creating a class and using a list for a stack?

I don't understand the difference between just using a list and appending and popping from it or creating a class which does the same thing?
list = []
list.append(1)
list.append(2)
class myStack:
def __init__(self):
self.container = [] # You don't want to assign [] to self - when you do that, you're just assigning to a new local variable called `self`. You want your stack to *have* a list, not *be* a list.
def isEmpty(self):
return self.size() == 0 # While there's nothing wrong with self.container == [], there is a builtin function for that purpose, so we may as well use it. And while we're at it, it's often nice to use your own internal functions, so behavior is more consistent.
def push(self, item):
self.container.append(item) # appending to the *container*, not the instance itself.
def pop(self):
return self.container.pop() # pop from the container, this was fixed from the old version which was wrong
def peek(self):
if self.isEmpty():
raise Exception("Stack empty!")
return self.container[-1] # View element at top of the stack
def size(self):
return len(self.container) # length of the container
def show(self):
return self.container # display the entire stack as list
s = myStack()
s.push('1')
s.push('2')
It looks the same to me so wouldn't the first code be a better implementation of a stack?

Python sort with last added element in the queue

Actually my sort algorithm works, but there is a problem.
I have a class namely SortedItem which includes
def __init__(self, point, cost):
self.coordinate = point
self.cost = cost
and I have also priority queue which sorts the this SortedItem by its cost:
class PriorityQueue:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def sortComparatorByCost(self, item):
return item.cost
def enqueue(self, item):
self.items.append(item)
self.items.sort(key=self.sortComparatorByCost, reverse=True)
def dequeue(self):
return self.items.pop()
def returnQueueAsString(self):
queue_str = ""
for eachItem in self.items:
queue_str += str(eachItem) + " "
return queue_str
def isQueueContainsElement(self, element):
for eachElement in self.items:
if eachElement[0] == element:
return True
return False
The problem occurs here:
- I have defined some order to add queue. Let's say I am adding this objects to the queue:
obj1 = SortedItem((1,0), 10))
queue.enqueue(obj1)
obj2 = SortedItem((2,0), 15))
queue.enqueue(obj2)
obj3 = SortedItem((2,1), 15))
queue.enqueue(obj3)
Now I have to get objects from queue in this order (obj1, obj2, obj3).
However python built-in sort function sort these objects like this: (obj1, obj3, obj2) (because obj2 and obj3 has the same cost)
How can i solve this issue. I mean If 2 objects cost is the same, I should get the first added one.
Note that: I have just created a simple example of my problem. If you try this code you may get the objects in this order: obj1, obj2, obj3
Instead of sorting the items in reverse order and removing them from the right,
def enqueue(self, item):
self.items.append(item)
self.items.sort(key=self.sortComparatorByCost, reverse=True)
def dequeue(self):
return self.items.pop()
you could remove them from the left. That would avoid reversing the order of insertion of the items with the same cost.
def enqueue(self, item):
self.items.append(item)
self.items.sort(key=self.sortComparatorByCost)
def dequeue(self):
return self.items.pop(0)
Removing items from the beginning of a list is not efficient, however, so you could better use a deque (replacing pop(0) by popleft()) to fix that. A deque on the other hand, has no in-place sort() method, so would need to replace self.items.sort() by self.items = deque(sorted(self.items)) as well.

Did I overuse threading locks in my custom list class?

I am writing a Python module in which two threads access one list. One thread adds 500 items to the list per second, and the other thread reads the list at an irregular interval. I want to make a thread-safe "list" class to avoid having to use locks every time I read or write to the list (suggested by this answer to a previous question on SO).
Here is my first go at a thread-safe list class (with help from these previous SO answers: 1 and 2). Are there any methods that should be locked that are not currently locked, or any methods that do not require a lock that are currently locked?
import collections
import threading
class ThreadSafeList(collections.MutableSequence):
"""Thread-safe list class."""
def __init__(self, iterable=None):
if iterable is None:
self._list = list()
else:
self._list = list(iterable)
self.rlock = threading.RLock()
def __len__(self): return len(self._list)
def __str__(self): return self.__repr__()
def __repr__(self): return "{}".format(self._list)
def __getitem__(self, i): return self._list[i]
def __setitem__(self, index, value):
with self.rlock:
self._list[index] = value
def __delitem__(self, i):
with self.rlock:
del self._list[i]
def __iter__(self):
with self.rlock:
for elem in self._list:
yield elem
def insert(self, index, value):
with self.rlock:
self._list.insert(index, value)

Stacks iteration python3

Ok so im trying to input a word in a stack and I want to print all of them after I input a string. So I can only print them one at a time. I tried using a for loop outside but Stacks are apparently not iterable. So I iterating it inside the stack. It still is not working.
class Stack:
def __init__(self):
self.items = []
def push(self,items):
self.items.insert(0,items)
def pop(self):
for x in self.items:
print( self.items.pop(0))
def show(self):
print (self.items)
s = Stack()
s.show()
placed = input("enter")
item = s.pop()
print(item, "is on top", s)
Give your Stack class a __len__ method, this will make testing if the stack is empty easier:
class Stack:
def __init__(self):
self.items = []
def push(self,item):
self.items.append(item)
def pop(self):
return self.items.pop()
def show(self):
print (self.items)
def __len__(self):
return len(self.items)
stack = Stack()
stack.push('World!')
stack.push('Hello')
while stack: # tests the length through __len__
print(stack.pop())
Note that I simply .append() to the end of the .items list, then later on .pop() (no arguments) again, removing from the end of the list.
To make your class an iterable type, you'd need to add at least an __iter__ method, optionally together with a .__next__() method:
class Stack:
# rest elided
def __iter__(self):
return self
def next(self):
try:
return self.items.pop()
except IndexError: # empty
raise StopIteration # signal iterator is done

Categories