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
Related
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))
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?
class Queue:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def enqueue(self, item):
self.items.append(item)
def dequeue(self):
if self.items == []:
raise IndexError('The queue is empty.')
return self.items.pop()
def size(self):
return len(self.items)
def __str__(self):
return "Queue: " + (str(self.items))
def enqueue_list(self, list):
for i in list:
self.items.append(i)
return self.items
def splice(self, second_queue):
for i in second_queue:
self.items.enqueue(i)
return self.items
Hi there,
What I am trying to do is at the bottom in the splice method. I want to iterate through a second queue and add it to the end of the original one. I can't find out how I can iterate through a queue without causing an error however. Should I change second_queue into a list somehow first?
Original exception was:
Traceback (most recent call last):
File "prog.python3", line 74, in <module>
my_queue.splice(another_queue)
File "prog.python3", line 28, in splice
for i in second_queue:
TypeError: 'Queue' object is not iterable
Instances of your class Queue are not iterable.
They hold a list items, but Python does not know that it should iterate over that list when you employ a for loop over a Queue instance.
To delegate the iteration to the wrapped list, simply add a method
def __iter__(self):
return iter(self.items)
Demo with fixed class:
>>> q = Queue()
>>> q.enqueue(1)
>>> q.enqueue(2)
>>> q.items
[1, 2]
>>>
>>> for item in q:
... print(item)
...
1
2
I'm a complete beginner for programming. I created the class "queue",added some elements to it and tried to print each element of the queue as following but I couldn't. Please help me!
Thanks in advance!
edited for formatting
class queue:
def __init__(self):
self.items = []
def isempty(self):
return self.items == []
def enqueue(self,item):
self.items.insert(0,item)
def dequeue(self):
return self.items.pop()
def size(self):
return len(self.items)
def peek(self):
return self.items[len(self.items)-1]
q=queue()
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
for n in q():
print "This time, it's: "+ str(n)
When you create your own class implementation then you you have to define each and every behaviour of that class, the for loop is only applicable to iterables and to make your object iterable you need to define __iter__ method inside your class which would be called implicitly whenever you try to iterate over your object.
class queue:
def __init__(self):
self.items = []
def isempty(self):
return self.items == []
def enqueue(self,item):
self.items.insert(0,item)
def dequeue(self):
return self.items.pop()
def size(self):
return len(self.items)
def peek(self):
return self.items[len(self.items)-1]
def __iter__(self):
for i in self.items:
yield i
q=queue()
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
for n in q:
print "This time, it's: "+ str(n)
You are iterating through a non-sequence type:
for n in q():
print "This time, it's: "+ str(n)
This is the correct way:
for n in q.items:
print "This time, it's: "+ str(n)
You either need to define an __iter__ method to make your queue iterable, or you need to modify the loop to use your defined methods.
Here q is an instance of class queue. Also q is not callable.And items is the instance variable.So you have to use
for n in q.items:
print "This time, it's: "+ str(n)
Defining the __getitem__ magic method is possibly the simplest way to make your queue iterable.
class queue:
def __init__(self):
self.items = []
def isempty(self):
return self.items == []
def enqueue(self,item):
self.items.insert(0, item)
def dequeue(self):
return self.items.pop()
def size(self):
return len(self.items)
def peek(self):
return self.items[len(self.items)-1]
def __getitem__(self, i):
return self.items[i]
q=queue()
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
for n in q:
print "This time, it's: "+ str(n)
Also don't use for n in q():. Thats going to try and call your queue object as a function. As q is not a function, it fails.
As a consequence of implementing __getitem__, you are also able to reference the elements in your queue by index directly on the queue object.
e.g.
print q[0]
prints
3
Well you have to enqueue something in the queue to print it out!
this code after your class
q=queue()
q.enqueue(3)
q.enqueue(4)
q.enqueue(55)
for n in q():
print "This time, it's: "+ str(n)
You never put anything in the queue. So it is empty. Therefore nothing gets printed. Try adding some objects (call the function q.enqueue) and then perhaps they will print.
In my case when import Queue the answer selected above doesn't work.
What really works is that:
for i in q.queue:
print i
OK so my actual code is somewhat elaborate but I am illustrating the problem that I am having with the following example code:
I have a class that has a list as one of its instance variable. I want the class to be an iterable and return the next element in the list when next is called in the for loop.
So I have as follows:
class SimplaWannaBeIteratable(object):
def __init__(self, list_to_iter, **kwargs)
self._list = list_to_iter
self._item = None
#... other code to initialize
def __iter__(self):
return self
def next(self):
self._item= next(self._list)
return self._item
def current(self):
#So that other uses cases have the access to the current member
return self._current
However if I do the following:
iter_item = SimplaWannaBeIteratable([1,2,3,4,5])
for item in iter_item:
return item
I get:
list object is not an iterator.
If I change the next as follows:
def next(self):
self._item= next(iter((self._list)))
return self._item
I get infinite output.
Can anyone tell me what I need to do to accomplish the task I want to do and why the code above is not working?
From what I understand every time next is called the iterator object associated with the list is called and its next is return. so why can't my list find its iterator?
You need an iterator to iterator over a list. A list itself is not an iterator so you cannot call next() on it.
class SimplaWannaBeIteratable(object):
def __init__(self, list_to_iter, **kwargs):
self._list = list_to_iter
self._item = None
def __iter__(self):
self._iter = iter(self._list) # create/initialize the iterator
return self
def __next__(self): # using the Python 3.x name
self._item = next(self._iter) # use the iterator
return self._item
# ...
You are calling next on self._list, which is a list, not an iterator. next only advances iterators, it does not set up an iterator from an iterable.
def __init__(self, ...):
# ...
self._iterator = iter(self._list)
def next(self):
self._item = next(self._iterator)
return self._item
Regarding your edit, you are getting an infinite recursion because you are calling next on a fresh iterator each time, rather than the same iterator. So you are losing the state of the iterator. Again, see my example above, which sets up the iterator once.
The __next__ special method that you are trying to implement is used to control iteration over a container-like class at each progressive step. If you do not need this functionality and simply want to make your class iterable, omit the method and return iter(self._list) from __iter__:
class SimplaWannaBeIteratable(object):
def __init__(self, list_to_iter, **kwargs):
self._list = list_to_iter
self._item = None
def __iter__(self):
return iter(self._list)
def current(self):
return self._current
Demo:
>>> iter_item = SimplaWannaBeIteratable([1,2,3,4,5])
>>> for item in iter_item:
... item
...
1
2
3
4
5
>>>