Python overwriting and resizing list - python

I want to create a list in python of a fixed size, let's so 3 to begin with. I have a method that writes data to this list every time the method is called, I want to add this to the list until the list is full, once the list is full it should start overwriting the data in the list in ascending order (e.g. starting with element 0). I also want to add a function which will increase the length of the array, i.e. if the method is called the array will increase from size 3 to size 4. How do I go about doing either of these?

This simple solution should do:
def add(l, item, max_len):
l.insert(0, item)
return l[:max_len]
l = ["banana", "peanut", "bicycle", "window"]
l = add(l, "monkey", 3)
print(newlist)
prints:
> ['monkey', 'banana', 'peanut']
The the list to edit (l), the item to add and the max size (max_len) of the list are arguments.
The item will then be added at index 0, while the list is limited to max_len.

This should do the trick. There are tons of prebuilt modules that have similar functionality but I thought it would be best if you could visualize the process!
class SizedList(list):
def __init__(self, size):
list().__init__(self)
self.__size = size
self.__wrap_location = 0
self.len = len(self)
def append(self, *args, **kwargs):
self.len = len(self)
if self.len >= self.__size:
if self.__wrap_location == self.__size-1:
self.__wrap_location = 0
self.__wrap_location += 1
self.pop(self.__wrap_location)
return self.insert(self.__wrap_location-1, *args)
return list.append(self, *args, **kwargs)
def increase_size(self, amount=1):
self.__size += 1

Here is how I went about it in the end. I set a variable called length_list which was originally set at 3.
def add_to_list():
if len(list) < length_list:
append
else:
del list[0]
self.add_to_list()
def increase_length():
length_list += 1

Related

How to keep ordered unique items in a size limited structure?

I need to save stream of elements in a size limited list. There may be duplicate elements in the stream but I need to just keep the unique ones. Also when the size of my list exceeds a specified limit, I need to remove the oldest element and add the new one.
I already have tried set and list. The problem with set is that it is not size limited and if I want to remove the oldest element I have no idea how to retrieve it because set is unordered; however, it solves the problem of uniqueness.
On the other hand list keeps the order of items, but I need to check for possible duplicates whenever I want to insert a new element and this can cost a lot of time. Also list is not size limited as well as set.
My third option could be collections.deque but I don't know if it keeps the order or not. And is there any way to keep the items in collections.deque unique?
These are examples of my codes for list:
ids = list()
for item in stream:
if item not in ids:
ids.append(item)
if len(ids) >= len_limit:
del ids[0]
and set:
ids = set()
for item in stream:
ids.add(item)
if len(ids) >= len_limit:
ids.remove(list(ids)[0])
You can write your own class which keeps both deque ans set:
import collections
class Structure:
def __init__(self, size):
self.deque = collections.deque(maxlen=size)
self.set = set()
def append(self, value):
if value not in self.set:
if len(self.deque) == self.deque.maxlen:
discard = self.deque.popleft()
self.set.discard(discard)
self.deque.append(value)
self.set.add(value)
s = Structure(2)
s.append(1)
s.append(2)
s.append(3)
s.append(3)
print(s.deque) # deque([2, 3], maxlen=2)
You may want to look into using the orderedset package. It is available via pip or conda. It is a very fast Cpython library that implements an ordered set.
pip install orderedset
or
conda install orderedset -c conda-forge
You can subclass OrderedSet to create an object that has a maximum number of elements.
from orderedset import OrderedSet
class DequeSet(OrderedSet):
def __init__(self, *args, maxlen=0, **kwargs):
if not isinstance(maxlen, int):
raise TypeError('`maxlen` must be an integer.')
if not maxlen>=0:
raise ValueError('`maxlen` must not be negative.')
self._maxlen = maxlen
if maxlen:
args = (args[0][-maxlen:],)
super().__init__(*args, **kwargs)
#property
def maxlen(self):
return self._maxlen
def _checkpop(self):
if not self._maxlen:
return
while self.__len__() > self._maxlen:
self.pop(last=False)
def __getattr__(self, attr):
self._checkpop()
return getattr(self, attr)
# this will truncate to the last 3 elements
ds = DequeSet('abcd', maxlen=3)
ds
3 returns:
DequeSet(['b', 'c', 'd'])
ds.add('e')
ds
# returns:
DequeSet(['c', 'd', 'e'])
I have created a simple queue,with a list. Also i have arranged the conditions in such a way that, there is less number of comparisions
class Queue:
def __init__(self,size):
self.elements = []
self.max_size = size
def put(self,elem):
if(elem in self.elements):
return
elif(len(self.elements) < self.max_size):
self.elements.append(elem)
else:
self.elements = self.elements[1:]+[elem]
def __str__(self):
return self.elements.__str__()
q=Queue(3)
q.put(1)
print(q)
q.put(2)
print(q)
q.put(2)
print(q)
q.put(3)
print(q)
q.put(3)
print(q)
q.put(4)
print(q)

Which is the cleaner way to get a Python #property as a list with particular conditions?

Now I have the source code above:
class Stats(object):
def __init__(self):
self._pending = []
self._done = []
#property
def pending(self):
return self._pending
The way those lists are filled is not important for my question.
The situation is that I'm getting a sublist of these lists this way:
stats = Stats()
// code to fill the lists
stats.pending[2:10]
The problem here is that I expect to get as many elements as I retrieved.
In the example above I expect a sublist that contains 8 elements (10-2).
Of course, actually I'll get less than 8 elements if the list is shorter.
So, what I need is:
When the list has enough items, it returns the corresponding sublist.
When the list is shorter, it returns a sublist with the expected length, filled with the last elements of the original lists and a default value (for example None) for the extra items.
This way, if I did:
pending_tasks = stats.pending[44:46]
And the pending list only contains 30 elements, it should returns a list of two default elements, for example: [None, None]; instead of an empty list ([]) which is the default behaviour of the lists.
I guess I already know how to do it inside a normal method/function, but I want to do it in the most clean way, trying to follow the #property approach, if possible.
Thanks a lot!
This is not easy to do because the slicing operation is what you want to modify, and that happens after the original list has been returned by the property. It's not impossible though, you'll just need to wrap the regular list with another object that will take care of padding the slices for you. How easy or difficult that will be may depend on how much of the list interface you need your wrapper to implement. If you only need indexing and slicing, it's really easy:
class PadSlice(object):
def __init__(self, lst, default_value=None):
self.lst = lst
self.default_value
def __getitem__(self, index):
item = getitem(self.lst, index)
if isinstance(index, slice):
expected_length = (index.stop - index.start) // (index.step or 1)
if len(item) != expected_length:
item.extend([default_value] * (expected_length - len(item)))
return item
This code probably won't work right for negative step slices, or for slices that don't specify one of the end points (it does have logic to detect an omitted step, since that's common). If this was important to you, you could probably fix up those corner cases.
This is not easy. How would the object (list) you return know how it will be sliced later? You could subclass list, however, and override __getitem__ and __getslice__ (Python2 only):
class L(list):
def __getitem__(self, key):
if isinstance(key, slice):
return [list(self)[i] if 0 <= i < len(self) else None for i in xrange(key.start, key.stop, key.step or 1)]
return list(self)[key]
def __getslice__(self, i, j):
return self.__getitem__(slice(i, j))
This will pad all slices with None, fully compatible with negative indexing and steps != 1. And in your property, return an L version of the actual list:
#property
def pending(self):
return L(self._pending)
You can construct a new class, which is a subclass of list. Then you can overload the __getitem__ magic method to overload [] operator to the appropriate behavior. Consider this subclass of list called MyList:
class MyList(list):
def __getitem__(self, index):
"""Modify index [] operator"""
result = super(MyList, self).__getitem__(index)
if isinstance(index, slice):
# Get sublist length.
if index.step: # Check for zero to avoid divide by zero error
sublist_len = (index.stop - index.start) // index.step
else:
sublist_len = (index.stop - index.start)
# If sublist length is greater (or list is shorter), then extend
# the list to length requested with default value of None
if sublist_len > len(self) or index.start > len(self):
result.extend([None for _ in range(sublist_len - len(result))])
return result
Then you can just change the pending method to return a MyList type instead of list.
class Stats(object):
#property
def pending(self):
return MyList(self._pending)
Hopefully this helps.

Python binary search recursive if possible

class SortedList:
theList = []
def add(self, number):
self.theList.append(number)
return self.theList
def remove(self, number):
self.theList.remove(number)
return self.theList
def printList(self):
return print(self.theList)
def binarSearch(self, number):
middle = (len(self.theList)//2)
end = len(self.theList)
if end != 0:
if int(self.theList[middle]) == int(number):
return print("The number is found in the list at place",middle+1)
elif int(self.theList[middle]) < int(number):
self.theList = self.theList[middle:]
return self.binarSearch(number)
elif int(self.theList[middle]) > int(number):
self.theList = self.theList[:middle]
return self.binarSearch(number)
else:
return print("The list is empty")
sorted = SortedList() #create a SortedList object
sorted.add("1")
sorted.add("2")
sorted.add("3")
sorted.add("4")
sorted.add("5")
sorted.add("6")
sorted.printList()
sorted.binarSearch(3)
I cannot use additional parameters I mut use only self and number. I want to make it recursive but if it is hard you can answer as normal.
This code works good until the number 4. When I give 4 for searching it says it is in place 2 and it continues saying two after 4. I have tried adding other numbers but it is same
Python already has a great module bisect which performs a binary search for sorted lists:
import bisect
l = [2,3,1,5,6,7,9,8,4]
print(bisect.bisect(l, 4)) # Output: 3
Familiarize yourself with this library:
https://docs.python.org/3.5/library/bisect.html
Just a hint: You can use additional parameters if you give them default values. Your method signature would look like this:
def binarSearch(self, number, start=0, end=len(self.theList)):
So it could still be called like sorted.binarySearch(5) but would internally be able to pass the state correctly.

Recursive function to count all items in a nested list?

I'm trying to create a method that will count all the items in a nested list. So count([[3, 2] , [2]]) == 3. However, it's a Class attribute so I can't just simply do:
def count(L, target):
s = 0
for i in L:
if isinstance(i, list):
s += count(i, target)
else:
if i == target:
s += 1
return s
Rather, I tried to do this, but I get a max recursion depth error. I'm not sure why. Before you look at the code, there's a few things to keep in mind: (1) I expect the base list given to only contain lists so it will have the format: [ [], ]. Also (2) the sub lists will not contain anything except items: [ [item, item], [item] ] :
def count(self, stack=None):
n = 0
if stack:
n += len(stack)
else:
for i in self._items:
if isinstance(i, list):
n += self.count(i)
return n
if stack:
Empty lists are considered false in a boolean context. You want if stack is not None.
Why use recursion, though? You don't need it.
def count(self):
return sum(len(item) for item in self._items)
If your lists are only nested one level deep, this is easy.
class MyClass:
def __init__(self, items):
self.items = items
def count(self):
return sum(len(x) for x in self.items)
a = MyClass([[3,2],[2]])
b = MyClass([[1,2,3],[4,5,6],[7],[]])
print(a.count()) # 3
print(b.count()) # 7

Dynamic list that automatically expands

How can I make a Python equivalent of pdtolist from Pop-11?
Assume I have a generator called g that returns (say) integers one at a time. I'd like to construct a list a that grows automatically as I ask for values beyond the current end of the list. For example:
print a # => [ 0, 1, 2, g]
print a[0] # => 0
print a[1] # => 1
print a[2] # => 2
# (obvious enough up to here)
print a[6] # => 6
print a # => [ 0, 1, 2, 3, 4, 5, 6, g]
# list has automatically expanded
a = a[4:] # discard some previous values
print a # => [ 4, 5, 6, g]
print a[0] # => 4
Terminology - to anticipate a likely misunderstanding: a list is a "dynamic array" but that's not what I mean; I'd like a "dynamic list" in a more abstract sense.
To explain the motivation better, suppose you have 999999999 items to process. Trying to fit all those into memory (in a normal list) all at once would be a challenge. A generator solves that part of the problem by presenting them one at a time; each one created on demand or read individually from disk. But suppose during processing you want to refer to some recent values, not just the current one? You could remember the last (say) ten values in a separate list. But a dynamic list is better, as it remembers them automatically.
This might get you started:
class DynamicList(list):
def __init__(self, gen):
self._gen = gen
def __getitem__(self, index):
while index >= len(self):
self.append(next(self._gen))
return super(DynamicList, self).__getitem__(index)
You'll need to add some special handling for slices (currently, they just return a normal list, so you lose the dynamic behavior). Also, if you want the generator itself to be a list item, that'll add a bit of complexity.
Just answered another similar question and decided to update my answer for you
hows this?
class dynamic_list(list):
def __init__(self,num_gen):
self._num_gen = num_gen
def __getitem__(self,index):
if isinstance(index, int):
self.expandfor(index)
return super(dynamic_list,self).__getitem__(index)
elif isinstance(index, slice):
if index.stop<index.start:
return super(dynamic_list,self).__getitem__(index)
else:
self.expandfor(index.stop if abs(index.stop)>abs(index.start) else index.start)
return super(dynamic_list,self).__getitem__(index)
def __setitem__(self,index,value):
if isinstance(index, int):
self.expandfor(index)
return super(dynamic_list,self).__setitem__(index,value)
elif isinstance(index, slice):
if index.stop<index.start:
return super(dynamic_list,self).__setitem__(index,value)
else:
self.expandfor(index.stop if abs(index.stop)>abs(index.start) else index.start)
return super(dynamic_list,self).__setitem__(index,value)
def expandfor(self,index):
rng = []
if abs(index)>len(self)-1:
if index<0:
rng = xrange(abs(index)-len(self))
else:
rng = xrange(abs(index)-len(self)+1)
for i in rng:
self.append(self._num_gen.next())
Many thanks to all who contributed ideas! Here's what I have gathered together from all the responses. This retains most functionality from the normal list class, adding additional behaviours where necessary to meet additional requirements.
class DynamicList(list):
def __init__(self, gen):
self.gen = gen
def __getitem__(self, index):
while index >= len(self):
self.append(next(self.gen))
return super(DynamicList, self).__getitem__(index)
def __getslice__(self, start, stop):
# treat request for "last" item as "most recently fetched"
if stop == 2147483647: stop = len(self)
while stop > len(self):
self.append(next(self.gen))
return super(DynamicList, self).__getslice__(start, stop)
def __iter__(self):
return self
def next(self):
n = next(self.gen)
self.append(n)
return n
a = DynamicList(iter(xrange(10)))
Previously generated values can be accessed individually as items or slices. The recorded history expands as necessary if the requested item(s) are beyond the current end of the list. The entire recorded history can be accessed all at once, using print a, or assigned to a normal list using b = a[:]. A slice of the recorded history can be deleted using del a[0:4]. You can iterate over the whole list using for, deleting as you go, or whenever it suits. Should you reach the end of the generated values, StopIteration is raised.
Some awkwardness remains. Assignments like a = a[0:4] successfully truncate the history, but the resulting list no longer auto-expands. Instead use del a[0:4] to retain the automatic growth properties. Also, I'm not completely happy with having to recognise a magic value, 2147483647, representing the most recent item.
Thanks for this thread; it helped me solve my own problem. Mine was a bit simpler: I wanted a list that automatically extended if indexed past its current length --> allow reading and writing past current length. If reading past current length, return 0 values.
Maybe this helps someone:
class DynamicList(list):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def __getitem__(self, idx):
self.expand(idx)
return super().__getitem__(idx)
def __setitem__(self, idx, val):
self.expand(idx)
return super().__setitem__(idx, val)
def expand(self, idx):
if isinstance(idx, int):
idx += 1
elif isinstance(idx, slice):
idx = max(idx.start, idx.stop)
if idx > len(self):
self.extend([0] * (idx - len(self)))

Categories