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
>>>
Related
I have some question about good practice.
So say we have some class which we want to use as iterator.
It class should returns first some header and then returns its blocks.
I see two ways for this case:
The first one is "classic". Return in iter methods self and put some logic in next method, like:
def __next(self):
if not self._header_was_returned:
self._header_was_returned = True
return self._header
if self._index >= self._count_blocks:
raise StopIteration
block = self._blocks[self._index]
self._index += 1
return block
Or another one, in this case used less code, just implenet iter method:
def __iter__(self):
yeild self._header
for block in self._blocks:
yield block
Another case is create some class IteratorForMyBlockClass and implements ``_next``` method there, but it similar for the first case.
Updated:
From "Fluent Python" (Chapter 14).
Main class is itarable (but not iterator).
Main class should return some IteratorClass in __iter__ method.
And in Iterator class I should put some logic:
class MainClass:
def __init__(self, header, blocks):
self._header = header
self._blocks = blocks
def __iter__(self):
return MainClassIterator(self._header, self._blocks)
class MainClassIterator:
def __init__(self, header, blocks):
self._header = header
self._blocks = blocks
self._index = 0
self._header_was_returned = False
def __iter__(self):
return self
def __next__(self):
if not self._header_was_returned:
self._header_was_returned = True
return self._header
if self._index >= len(self._blocks):
raise StopIteration
block = self._blocks[self._index]
self._index += 1
return block
Is that a good solution?
You don't really need a MainClassIterator. Since a generator function always returns an iterator, an easy solution would be to put this __iter__ in MainClass:
def __iter__(self):
yield self._header
yield from self._blocks
I was trying to write a simple countdown iterator of my own, I implemented a __iter__() function and the corresponding __next__() to support the iterator.I have used a yield function inside the __next__() function to return a new value everytime I iterate over the object.
When I use yield, the code goes over in an infinite loop as compared to using return statement.
Following is my code:
class MyIterator():
def __init__(self,value):
self.value = value
def __iter__(self):
return self
def __next__(self):
print("In the next function")
if self.value > 0:
yield self.value
self.value -= 1
else:
raise StopIteration("Failed to proceed to the next step")
if __name__ == '__main__':
myIt = MyIterator(10)
for i in myIt:
print(i)
And the O/P is as follows:
<generator object __next__ at 0x101181990>
<generator object __next__ at 0x1011818e0>
<generator object __next__ at 0x101181990>
<generator object __next__ at 0x1011818e0>
and so on for infinite times....
Your __next__ method should not itself be a generator. Replace yield with return:
def __next__(self):
print("In the next function")
if self.value > 0:
return_value = self.value
self.value -= 1
return return_value
else:
raise StopIteration("Failed to proceed to the next step")
Note that you still need to decrease self.value after determining what is to be returned, hence the use of a separate return_value variable.
Any function (or method) with yield in it will produce a generator object when called, and that generator is then the iterable. Such an object then has an __iter__ method that returns self and a __next__ method that will produce the next value when called. This is why you see the <generator object __next__ at 0x1011818e0> object being printed each time __next__ is called.
However, for your object to be an iterable itself, your __next__ method should instead return the next value in the sequence. It'll be called repeatedly until it raises StopIteration. This is different from using yield, it should return immediately, not defer until later!
Demo:
>>> class MyIterator():
... def __init__(self,value):
... self.value = value
... def __iter__(self):
... return self
... def __next__(self):
... print("In the next function")
... if self.value > 0:
... return_value = self.value
... self.value -= 1
... return return_value
... else:
... raise StopIteration("Failed to proceed to the next step")
...
>>> myIt = MyIterator(10)
>>> for i in myIt:
... print(i)
...
In the next function
10
In the next function
9
In the next function
8
In the next function
7
In the next function
6
In the next function
5
In the next function
4
In the next function
3
In the next function
2
In the next function
1
In the next function
If you wanted to use a generator function, make __iter__ the generator, and use a loop:
class MyIterator():
def __init__(self,value):
self.value = value
def __iter__(self):
value = self.value
while value > 0:
yield value
value -= 1
However, this makes your MyIterator class an iterable, not an iterator. Instead, each time you use a for loop a new iterator is created (the __iter__ generator object) that is then iterated over. Using __next__ makes your object an iterator that can only be iterated over once.
There is a bit of confusion here. When you using yield, you don't need to create an iterator class. The generator already is an iterator.
So, change the yield to a return and it will do what you want :-)
Also, you will need to update self.value before the return. Here is what the fixed-up code looks like:
class MyIterator():
def __init__(self,value):
self.value = value
def __iter__(self):
return self
def __next__(self):
print("In the next function")
value = self.value
if value > 0:
self.value -= 1
return value
else:
raise StopIteration("Failed to proceed to the next step")
And here is how to do the same thing with a generator:
def my_iterator(value):
while value > 0:
yield value
value -= 1
As you can see, generators make a programmer's life much easier :-)
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)
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
I am interested in using the python list object, but with slightly altered functionality. In particular, I would like the list to be 1-indexed instead of 0-indexed. E.g.:
>> mylist = MyList()
>> mylist.extend([1,2,3,4,5])
>> print mylist[1]
output should be: 1
But when I changed the __getitem__() and __setitem__() methods to do this, I was getting a RuntimeError: maximum recursion depth exceeded error. I tinkered around with these methods a lot but this is basically what I had in there:
class MyList(list):
def __getitem__(self, key):
return self[key-1]
def __setitem__(self, key, item):
self[key-1] = item
I guess the problem is that self[key-1] is itself calling the same method it's defining. If so, how do I make it use the list() method instead of the MyList() method? I tried using super[key-1] instead of self[key-1] but that resulted in the complaint TypeError: 'type' object is unsubscriptable
Any ideas? Also if you could point me at a good tutorial for this that'd be great!
Thanks!
Use the super() function to call the method of the base class, or invoke the method directly:
class MyList(list):
def __getitem__(self, key):
return list.__getitem__(self, key-1)
or
class MyList(list):
def __getitem__(self, key):
return super(MyList, self).__getitem__(key-1)
However, this will not change the behavior of other list methods. For example, index remains unchanged, which can lead to unexpected results:
numbers = MyList()
numbers.append("one")
numbers.append("two")
print numbers.index('one')
>>> 1
print numbers[numbers.index('one')]
>>> 'two'
Instead, subclass integer using the same method to define all numbers to be minus one from what you set them to. Voila.
Sorry, I had to. It's like the joke about Microsoft defining dark as the standard.
You can avoid violating the Liskov Substitution principle by creating a class that inherits from collections.MutableSequence, which is an abstract class. It would look something like this:
def indexing_decorator(func):
def decorated(self, index, *args):
if index == 0:
raise IndexError('Indices start from 1')
elif index > 0:
index -= 1
return func(self, index, *args)
return decorated
class MyList(collections.MutableSequence):
def __init__(self):
self._inner_list = list()
def __len__(self):
return len(self._inner_list)
#indexing_decorator
def __delitem__(self, index):
self._inner_list.__delitem__(index)
#indexing_decorator
def insert(self, index, value):
self._inner_list.insert(index, value)
#indexing_decorator
def __setitem__(self, index, value):
self._inner_list.__setitem__(index, value)
#indexing_decorator
def __getitem__(self, index):
return self._inner_list.__getitem__(index)
def append(self, value):
self.insert(len(self) + 1, value)
class ListExt(list):
def extendX(self, l):
if l:
self.extend(l)