Dynamic Generator in Python - python

I need to create a service that can manage a dynamic resource (I'm thinking on a dynamic csv that is growing. In this case I used a list to make more easy to understand), returning the next item from an iterable.
If the list is finished, the output would be False and I need to wait that an element is added.
When the element if finally added, the generator should yield the new added item (this is the part that I can not understand how to do).
a_lst = [1, 2]
class DynamicGenerator:
"""
Generic Generator that return item by item from a_lst and
can append a new item when needed.
"""
def __init__(self):
global a_lst
def gen_lst(self):
for item in a_lst:
yield item
def grow_lst(self, new_item):
a_lst.append(new_item)
class AvailableNextIterator:
"""
Return next item from iterable if it's available,
else return False.
"""
def __init__(self, iterable):
self.iterator = iterable
def __next__(self):
return next(self.iterator, False)
dg = DynamicGenerator()
gl = dg.gen_lst()
it = AvailableNextIterator(gl)
print(it.__next__())
print(it.__next__())
print(it.__next__())
dg.grow_lst(3)
print(it.__next__())
This is the code output:
1
2
False
False
This is the output that I need:
1
2
False
3

Very simply, you exhausted the iteration before you added the last item; you already tripped the end-of-data exception for gen_list. You've dropped out of the loop, and there's no provision to re-enter. You can see this with a little simple tracing in that method:
def gen_lst(self):
for item in a_lst:
yield item
print("gen_lst is out of data")
yield "over the cliff's edge ..."
Now, the output is
1
2
gen_lst is out of data
over the cliff's edge ...
False
You seem to want a FIFO queue; I suggest that you use the available data structure in the readily-available packages, rather than trying to roll your own.
However, if this is a homework exercise:
Mark your question as such, as required by SO posting standards;
Research existing implementations.
Maintain the queue as an attribute of the instance, not an external variable. Your "generator" should check the queue directly, rather than depending on for to manage the iteration.

Related

Error when using __iter__ and __next__ with a Python linked list? [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 2 years ago.
Improve this question
def __iter__(self):
return self
def __next(self):
if self.head is None:
raise StopIteration
else:
return self.head.next
Basically I attempted these implementation of the methods based on a basic example for W3 schools. It doesn't like that iter is returning an object type of the LinkedList.
Error:
TypeError: iter() returned non-iterator of type 'LinkedList' linked list
If I try to return self.head, it doesn't like that it is a node object?
Error:
TypeError: iter() returned non-iterator of type 'Node'
I am trying to make my LinkedList iterable with a forloop, but am having a hard time.
Does anyone know the correct implentation to make a LinkedList iterable in Python? Similar questions on here do not make sense to me and the solutions don't work for my usage.
The dunder method for the next item is __next__ rather than __next, and you seem to be missing code that actually moves through the list as you iterate.
The normal method I use is to have a separate iterator class within the type to do the grunt work. In addition, the way you have your list set up makes the idea of an element class desirable as well (were you using a Python list, this would not be needed but, since you want an item and next "pointer", it's better as a separate class).
Let's start by creating the list class with an enclosed item class:
class MyList:
# Single element in the list.
class MyListElem:
def __init__(self, item, link_from = None, link_to = None):
# Set data, link from previous and to next if specified.
self._item = item
self._next = link_to
if link_from is not None:
link_from.change_link(self)
def change_link(self, item):
self._next = item
The element class has two methods, the first creating an element with links as needed. At the moment (see MyList.add() below), the to link is always None as we only append to the list.
Similarly, the from link is either None or the current tail, depending on whether we're adding the first or subsequent elements to the list.
These parameters are mostly here to cater for the possibility of later being able to insert at arbitrary places in the list.
Next is the iterator class, also part of the list class:
# Iterator over the list.
class MyListIter:
def __init__(self, head):
# Start at the head.
self._curr = head
def __iter__(self):
# To iterate over the iterator rather than collection.
return self
def __next__(self):
# Check more left, advance to next and return current.
if self._curr is None:
raise StopIteration
ret_val = self._curr._item
self._curr = self._curr._next
return ret_val
This is relatively simple, the initialiser just sets us up so that the first call will return the head. The __iter__ call allows you to retrieve an iterator from the collection and then iterate over that, rather than iterating directly over the collection. The difference being:
for iter in object: # iterate over object
pass
iter = object.__iter__() # iterate over iterator
for iter2 in iter:
pass
See here for more details on why this may be necessary.
The __next__ call is the "meat" of the iterator. It raises an exception if the iterator is finished. Otherwise it returns the current value, after advancing to the next.
And then the list class itself, a very simple one that only allows appending to the end, no deleting, and an iterator to process it:
# The list itself.
def __init__(self):
# Empty list to start with.
self._head = None
self._tail = None
def add(self, val):
# Append to the end of the list.
if self._head is None:
self._head = self.MyListElem(val)
self._tail = self._head
else:
self._tail = self.MyListElem(val, self._tail)
def __iter__(self):
return self.MyListIter(self._head)
It's really that easy (well, relatively easy). The following test code shows it in action:
x = MyList()
x.add(12345)
x.add(42)
x.add("Hello")
x.add([1, 2, 3])
for i in x:
print(i)
The output of that is, as expected:
12345
42
Hello
[1, 2, 3]

Fixing My Base 10 to Binary Converter

Right now, I have a base 10 to base 2 converter that works, however it always prints none at the end of every conversion.
base_two=0
def binary_recursion(base_ten):
global base_two
if base_ten==0:
print(base_two)
return
c=0
while base_ten//2**c>1:
c+=1
base_two+=10**c
if c==0:
print(base_two)
return
binary_recursion(base_ten-2**c)
I tried returning base_two as opposed to printing it, but that doesn't return a number, it also just returns None. Can anyone help me pinpoint my mistake?
def node(document_info, next_node):
return {'data': document_info, 'next': next_node}
def insert(head, new_document_info):
#insert new document into the linked list head
#returns the head of the modified list
if head is None:
return node(new_document_info, None)
if new_document_info[1] <= head['data'][1]:
return node(new_document_info, head)
head['next'] = insert(head['next'], new_document_info)
return head
Here's a slightly modified way of doing insertion sort, from my answer to you last question. You would start with head = None and then every time you add a print job do head = insert(head, document_info). Or after collecting all of your print jobs do something like
head = None
for document_info in list_queue:
head = insert(head, document_info)
You are not adding new element into queue. Assuming list_queue is a queue.
Queue has put function to add a new element into it.
def make_job():
temp_list=[]
list_queue = Queue()
for line in print_list:
if line[:6]=='submit': #If the command is submit, then the file must be placed in its
a=line.split() #correct spot in the linked list
del(a[0])
list_queue.put(a)#Contains file id, and the time required to print
temp_list.append(list_queue)
organize(list_queue) #Places the file in its correct spot in the linked list
else:
break
The Python queue module has a class called PriorityQueue that does exactly what you're looking for. For your situation, using it would look something like this:
class Job(object):
def __init__(self, name, print_time):
self.name = name
self.print_time = print_time
def __lt__(self, other):
return self.print_time < other.print_time
import queue as _queue # Need to rename the module so it doesn't conflict with your 'queue' function
my_queue = _queue.PriorityQueue()
def make_job():
for line in print_list:
if line[:6]=='submit':
a=line.split()
del(a[0])
new_job=queue(a) # queue(a) now returns a Job, e.g. Job('101', 40), instead of a 2-element list
my_queue.put(new_job)
else:
break
Once my_queue has been constructed, then repeated calls to my_queue.get() will return the Jobs ordered by print_time.
If you want to be able to inspect the contents of the queue without removing elements (get removes the element it returns), you could append the Jobs to a list and call list_queue.sort() after every insertion. If this is a performance concern, you could find the right spot in the list yourself and call list_queue.insert(i, a). Deferring to Python's list.sort has some advantages, though; namely, it's stable.
Lastly, if you don't want to define a new class, you could use sorted or list.sort with a custom sorting function. This takes the place of the __lt__ member I defined for Job.
new_job=queue(a) # queue(a) is your original function, which returns 2-element lists like ['101', 40]
list_queue.append(new_job)
list_queue.sort(key=lambda a,b: return a[1]<b[1])

Binary Searching and lists

class BinaryStringList():
def __init_(self):
self.item = []
def strAdd(self,item):
self.items.append(item)
def finditem(self, item):
if len(self)==0:
print("List is empty!")
else:
midpoint = len(self)//2
if self[midpoint]==item:
print("Item Found ", item)
else:
if item<self[midpoint]:
return finditem(self[:midpoint], item)
else:
return finditem(self[midpoint+1:], item)
So where I am finding I have an issue is when trying to add items to the list. If i do something like:
alist = BinaryStringList()
alist.strAdd("test1")
my code fails stating object has no attribute. Not sure why it is failing since I have almost the exact same code for another program except the find is using a sequential search where as this is a binary search.
You have multiple syntax errors in your code. Also recursion doesn't work that way, you need to have a base condition which returns. This solution will work, but I strongly suggest you to solve simpler problems using recursion to understand how it works.
class BinaryStringList:
def __init__(self): # You had 1 _ after init
self.items = [] # Typo, should have been items.
def strAdd(self,item):
self.items.append(item)
def finditem(self, item):
return self.binser(self.items, item)
def binser(self, items, item):
if len(items)==0:
return
midpoint = len(items)/2 # len(self) means nothing, it should be len(self.items)
if items[midpoint]==item:
return item
else:
if item<items[midpoint]:
return self.binser(items[:midpoint], item) #self[:midpoint] means nothing, you needed self.items[:midpoint]
else:
return self.binser(items[midpoint+1:], item)
binser = BinaryStringList()
binser.strAdd(1) # You added a string here. Your logic won't work with string.
binser.strAdd(2)
binser.strAdd(3)
binser.strAdd(5)
binser.strAdd(8)
binser.strAdd(9)
binser.strAdd(10)
print binser.finditem(1)
print binser.finditem(10)
print binser.finditem(5)
print binser.finditem(11)
(there are other ways of solving binary search too - i.e. iterative approach, passing low/high index values rather than splicing the input array). Try to solve binary search using those two approaches.
Binary search with passing low/high index values, your signature for binser will look like: def binser(self, low, high, item):
Your code is failing because you misspelled __init__. You need two underscores on each side, or it's just a weirdly named method. Since you lack a __init__, the default __init__ (which sets no attributes) is used, and you don't have an item or items attribute. You need to fix the __init__, and use a consistent name for items:
class BinaryStringList():
def __init__(self): # <-- Added extra trailing underscore
self.items = [] # Fixed name to be items, not item
You have many other problems here (you're not maintaining sorted order, so binary search won't work, you haven't implemented __getitem__ so self[midpoint] won't work so you'd need self.items[midpoint], lack of __len__ means len(self) won't work either, etc.), but the two issues above are what specifically makes you get the AttributeError.

How to use standard dict / list iterators with python gtk GenericTreeModel?

I have my own tree-isch data structure in memory and want to use the
gtk GenericTreeModel to display it. On some levels in my tree I have
dictionaries, on some other levels lists.
(I do not want the TreeStore, because then I have to link /
copy things around. I want to operate on the original data.)
My idea is to use a dictionary / list iterator when implementing the
method on_get_iter(). For the top level element the method looks
like (some code skipped):
def on_get_iter(self, path):
return self.__my_data.get_dict().iteritems()
The method on_iter_next() is very simple:
def on_iter_next(self, rowref):
try:
return rowref.next()
except StopIteration:
return None
But then there is the need to get the data. This is done with
on_get_value(). I have no idea how to implement this:
def on_get_value(self, rowref, column):
# What to write here?
The documentation says, that the value of the current row should be
returned here. IMHO 'rowref.next()' does the wrong thing, because it
does not get the current but the next element.
IMHO there is no way to access the current element of an iterator (see
PEP 234).
Is there a way to use the GenericTreeModel with standard iterators
like dictionary or list iterators?
Update:
I found a workaround for this. Using the following iterator instead of the standard ones works for me; the constructor gets the standard dictionary / list iterator.
class GTMIterator:
def __init__(self, iterator):
self.__iterator = iterator
self.__current = None
self.next()
def next(self):
try:
self.__current = self.__iterator.next()
except StopIteration:
return None
return self.__current
def current(self):
return self.__current
But: IMHO this is only a workaround and not a 'clean' solution.
One the way to do this would be as follows:
def on_get_iter(self, path):
...
iterator = self.__my_data.get_dict().iteritems()
data = iterator.next()
return {'iterator': iterator, 'data': data}
def on_iter_next(self, rowref):
try:
iterator = rowref['iterator']
next_data = iterator.next()
return {'iterator': iterator, 'data': next_data}
except StopIteration:
return None
def on_get_value(self, rowref, column):
key, value = rowref['data']
return value[column]
The key here is that the on_* functions may return any user data object that
they wish. That object is automatically wrapped in a gtk.TreeIter using
gtk.GenericTreeModel.create_tree_iter and unwrapped again when calling
another on_* function using gtk.GenericTreeModel.get_user_data.
Hence, instead of returning just a python iterator, the key and value returned
by the last call to next method can be stored to access it in case it's
needed when calling to some other method.
You need to implement GtkTreeModel interface in your data-keeping object.
GtkTreeview use as model any GObject implementing that interface.
For futher references look at Overview of GtkTreeModel, GtkTreeView, and friends

Object Oriented Programming removing repeated items (Python)

I'm trying to remove an item from a sorted list. If the item is not in the list, then the list remains unchanged. If the item occurs multiple times, only one occurrence of the item is removed. Again, I'm not allowed to use build-in list functions, but for the time being, I'm just trying to get the code to just work!
class SortedList:
def __init__(self):
self.s_list = []
def insert(self, item):
self.s_list.append(item)
def remove(self, item):
finalSet=[]
for item in self.s_list:
if item not in finalSet:
finalSet.append(item)
return finalSet
def __str__(self):
return str(self.s_list)
Your remove function seems very confused.
def remove(self, item):
finalSet=[]
for item in self.s_list:
if item not in finalSet:
finalSet.append(item)
return finalSet
Why are you creating a new list, shouldn't you be modifying the existing list?
There are two different item in the function. One is a parameter to the function, the other is in the loop. The one in the loop replaces the parameter. Give them different names
You return with the list almost right away, you probably don't want to return until after the loop is completed. Your return statement is intended too far
Since you've confused two different variables by giving them the same name, I can't guess what you were actually trying to do inside the loop.
Other Question
Full page about topic

Categories

Resources