I need a list-like object that will "autogrow" whenever a slot number greater or equal to its length is accessed, filling up all the newly created slots with some pre-specified default value. E.g.:
# hypothetical DefaultList class
x = DefaultList(list('abc'), default='*')
x[6] = 'g'
print x[2], x[4], x[6], x[8] # should print 'c * g *'
Thanks!
PS. I know it is not hard to implement a class like this, but I avoid wheel-reinvention as much as possible, especially if a particularly efficient/well-designed wheel already exists.
PS2. A dict (or a collections.defaultdict) is not an acceptable implementation of the desired data structure. For why, see here: http://groups.google.com/group/comp.lang.python/msg/bcf360dfe8e868d1?hl=en
class DefaultList(list):
def __init__(self,*args,**kwargs):
list.__init__(self,*args)
self.default=kwargs.get('default',None)
def __getitem__(self,key):
# retrieving an item does not expand the list
if isinstance(key,slice):
return [self[elt] for elt in range(key.start,key.stop,key.step)]
else:
try:
return list.__getitem__(self,key)
except IndexError:
return self.default
def __setitem__(self,key,value):
# setting an item may expand the list
try:
list.__setitem__(self,key,value)
except IndexError:
self.extend([self.default]*(key-len(self)))
self.append(value)
x = DefaultList(list('abc'), default='*')
print(x)
# ['a', 'b', 'c']
x[6] = 'g'
print(x)
# ['a', 'b', 'c', '*', '*', '*', 'g']
print x[2], x[4], x[6], x[8] # should print 'c * g *'
# c * g *
print(x[2:9:2])
# ['c', '*', 'g', '*']
I would use a sparse data structure (1xn matrix).
You could always make a function that handles this:
def fillList(item, slot, myList):
length = len(myList)
if slot > length:
augmentation = [item for x in range(slot-length)]
myList.extend(augmentation)
else:
myList[slot] = item
Which while not a data structure, does accomplish what you want.
Using the idea of wheaties's solution and making a prettier interface:
You could inherit from list and overwrite the list 'getitem(index)' method which maps to [index] in your class. It should be something like this:
class GrowingList(list):
def __getitem__(self, index):
length = len(self)
# list is 0 indexed
if index >= length:
tail = [ self.default_value for x in range(index - length + 1)]
self.extend(tail)
return super(self.__class__, self).__getitem__(index)
This same code can be used if you don't extend the list, but just return some default value on invalid index
This preserves the whole list interface.
(This isn't a new answer; just a comment on unutbu's. It should really be possible to post stuff like this in comments; it isn't, so I have to post it as an answer.)
CombineListClasses and CombineListClasses2 inherit from two classes that both inherit from list. The behavior and doctests are straightforward, but break badly in the original version.
This is all standard practice in Python's data model; you almost never should be calling a base class method directly rather than via super.
class DefaultList(list):
"""
>>> x = DefaultList('abc', default='*')
>>> x
['a', 'b', 'c']
>>> x[6] = 'g'
>>> x
['a', 'b', 'c', '*', '*', '*', 'g']
>>> x[2], x[4], x[6], x[8] # should print 'c * g *'
('c', '*', 'g', '*')
>>> x[2:9:2]
['c', '*', 'g', '*']
>>> x = DefaultList()
>>> x[1] = 'a'
>>> x
[None, 'a']
>>> x = DefaultList(sequence=[1,2,3], default=5)
>>> x
[1, 2, 3]
>>> x[10]
5
"""
def __init__(self, *args, **kwargs):
if 'default' in kwargs:
self.default = kwargs['default']
del kwargs['default']
else:
self.default = None
super(DefaultList, self).__init__(*args, **kwargs)
def __getitem__(self, key):
# retrieving an item does not expand the list
if isinstance(key, slice):
return [self[elt] for elt in range(key.start, key.stop, key.step)]
else:
try:
return super(DefaultList, self).__getitem__(key)
except IndexError:
return self.default
def __setitem__(self, key, value):
# setting an item may expand the list
try:
super(DefaultList, self).__setitem__(key, value)
except IndexError:
self.extend([self.default]*(key-len(self)))
self.append(value)
# Another class that derives from list:
class AddMethodToList(list):
def __init__(self, *args, **kwargs):
self.value = kwargs['value']
del kwargs['value']
super(AddMethodToList, self).__init__(*args, **kwargs)
def new_method(self):
return self.value
# Derive from both classes.
class CombineListClasses(AddMethodToList, DefaultList):
"""
>>> a = CombineListClasses(default=10, sequence=[1,2,3], value=3)
>>> a.new_method()
3
>>> a[5] = 1
>>> a
[1, 2, 3, 10, 10, 1]
"""
pass
# Derive from both classes in reverse, reversing the call chain order.
class CombineListClasses2(DefaultList, AddMethodToList):
"""
>>> a = CombineListClasses2(default=10, sequence=[1,2,3], value=3)
>>> a.new_method()
3
>>> a[5] = 1
>>> a
[1, 2, 3, 10, 10, 1]
"""
pass
if __name__ == '__main__':
import doctest
print doctest.testmod()
Note that in Python 3, this is supported by the language directly:
class DefaultList(list):
def __init__(self, *args, default=None, **kwargs):
self.default = default
super(self).__init__(*args, **kwargs)
but that's not supported in Python 2. http://www.python.org/dev/peps/pep-3102
Related
I am working through the following exercise: I am implementing a class that represents sorted lists of basic types.
Currently:
class SortedList():
def __init__(self, input_list):
self.input_list= input_list
def add(self,value):
self.input_list.append(value)
return self.input_list
def concat(self):
return
def __repr__(self):
self.input_list.sort()
return str(self.input_list)
I make the following calls:
l1= SortedList(['z','l','a'])
print(l1)
l1.add('b')
print(l1)
l2= SortedList(['q','g'])
l3= l1.cocat(l2)
print(l3)
Everything behaves as expected until the l3 definition, since unsure how to define this type of function x.function(y) within a class.
The desired output from the last print statement is ['a','b','g','l','q','z']
You can use the + operator on lists which extends a list with another list, and then return a new instance of SortedList when concat() is called.
class SortedList:
def __init__(self, input_list):
self.input_list = sorted(input_list)
def add(self, value):
self.input_list.append(value)
self.input_list.sort()
return self.input_list
def concat(self, other):
merged = self.input_list + other.input_list
return SortedList(merged)
def __repr__(self):
return str(self.input_list)
l1 = SortedList(["z", "l", "a"])
print(l1)
# ['a', 'l', 'z']
print(l1.add("b"))
# ['a', 'b', 'l', 'z']
l2 = SortedList(["q", "g"])
l3 = l1.concat(l2)
print(l3)
# ['a', 'b', 'g', 'l', 'q', 'z']
In the following code, I'm able to add the first number, but I can't add the second number. Inside the class, self correctly updates to [1, 3], but the instance stays at [1]. Why is that and how do I fix it?
from bisect import bisect
class SortedList(list):
def __init__(self):
self = []
def add(self, x):
index = bisect(self, x)
if not self:
self.append(x)
else:
self = self + [x] + self[index:] # after the second call self = [1, 3]
pass
t = 1
for _ in range(t):
n, a, b = 24, 3, 5
if b == 1:
print('yes')
else:
sl = SortedList() # sl = []
st = set()
sl.add(1) # sl = [1]
st.add(1)
i = 0
while sl[i] < n: # when i == 1, there's an error because sl = [1]
num1 = sl[i] * a # num1 = 3
num2 = sl[i] + b
if num1 > sl[i] and num1 not in st:
sl.add(num1) # after this, sl = [1] still
st.add(num1)
if num2 > 1 and num2 not in st:
sl.add(num2)
st.add(num2)
if n in st:
break
i += 1
print('yes' if n in st else 'no')
Don't modify self, when you assign the resulting list to self you change its reference in the local context of the function. But the remote reference keep unchanged.
Better explained in Is it safe to replace a self object by another object of the same type in a method?
By sub-classing list:
import bisect
class SortedList(list):
def append(self, x):
if not self:
super(SortedList, self).append(x)
else:
idx = bisect.bisect(self, x)
self.insert(idx, x)
And then:
>>> import random
>>> l = SortedList()
>>> for i in range(10):
... l.append(random.randint(0, 100))
>>> print(l)
[5, 31, 50, 58, 69, 69, 70, 78, 85, 99]
In order to keep the list sorted you should also override some magic methods such __add__, __iadd__ ...
By wrapping list:
class SortedList(object):
def __init__(self):
self._data = []
def append(self, x):
if not self:
self._data = [x]
else:
idx = bisect.bisect(self, x)
# Here you can change self._data
# Even `insert` is better, because it don't need to copy the list
# you can do
self._data = self._data[:idx] + [x] + self._data[idx:]
But it's very partial, in order to have SortedList look like a list you have to implement the sequence protocol.
self = []
...
self = self + [x] + self[index:]
This doesn't do what you think it does. Whenever you write a = ... (without indexing on the left hand side, which is special) you don't actually change what a is. Instead, you just take whatever is on the right hand side and give it the name a, ignoring whatever a was before.
Example:
>>> a = [1, 2, 3]
>>> b = a
>>> a is b
True
>>> id(a), id(b)
(140484089176136, 140484089176136)
>>> a.append(4) # Modification.
>>> b
[1, 2, 3, 4]
>>> a = a + [5] # Modification? No! We create a new object and label it 'a'.
>>> a
[1, 2, 3, 4, 5]
>>> b
[1, 2, 3, 4]
>>> a is b
False
>>> id(a), id(b)
(140484995173192, 140484089176136)
What you want is bisect_left and to use list.insert:
index = bisect_left(li, x)
li.insert(index, x)
Thanks, everyone. I was able to get the code to work as expected by changing the class as follows:
class SortedList(list):
def add(self, x):
index = bisect(self, x)
self.insert(index, x)
I have this tree class, and I'm creating a function that will return a list of all the values in a tree.
This is the tree class:
class Tree:
"""
>>> t = Tree(3, [Tree(2, [Tree(5)]), Tree(4)])
>>> t.label
3
>>> t.branches[0].label
2
>>> t.branches[1].is_leaf()
True
"""
def __init__(self, label, branches=[]):
for b in branches:
assert isinstance(b, Tree)
self.label = label
self.branches = list(branches)
def is_leaf(self):
return not self.branches
def map(self, fn):
"""
Apply a function `fn` to each node in the tree and mutate the tree.
>>> t1 = Tree(1)
>>> t1.map(lambda x: x + 2)
>>> t1.map(lambda x : x * 4)
>>> t1.label
12
>>> t2 = Tree(3, [Tree(2, [Tree(5)]), Tree(4)])
>>> t2.map(lambda x: x * x)
>>> t2
Tree(9, [Tree(4, [Tree(25)]), Tree(16)])
"""
self.label = fn(self.label)
for b in self.branches:
b.map(fn)
def __contains__(self, e):
"""
Determine whether an element exists in the tree.
>>> t1 = Tree(1)
>>> 1 in t1
True
>>> 8 in t1
False
>>> t2 = Tree(3, [Tree(2, [Tree(5)]), Tree(4)])
>>> 6 in t2
False
>>> 5 in t2
True
"""
if self.label == e:
return True
for b in self.branches:
if e in b:
return True
return False
def __repr__(self):
if self.branches:
branch_str = ', ' + repr(self.branches)
else:
branch_str = ''
return 'Tree({0}{1})'.format(self.label, branch_str)
def __str__(self):
def print_tree(t, indent=0):
tree_str = ' ' * indent + str(t.label) + "\n"
for b in t.branches:
tree_str += print_tree(b, indent + 1)
return tree_str
return print_tree(self).rstrip()
And this is the function I've written:
def preorder(t):
"""Return a list of the entries in this tree in the order that they
would be visited by a preorder traversal (see problem description).
>>> numbers = Tree(1, [Tree(2), Tree(3, [Tree(4), Tree(5)]), Tree(6, [Tree(7)])])
>>> preorder(numbers)
[1, 2, 3, 4, 5, 6, 7]
>>> preorder(Tree(2, [Tree(4, [Tree(6)])]))
[2, 4, 6]
"""
result = []
result.append(t.label)
if t.is_leaf:
return result
else:
return result + [preorder(b) for b in t.branches]
However, this does not work. For the example given it just returns [1]. Could someone help me understand why?
By my logic, I'm creating a list, and adding the label of whatever branch/tree/leaf the function is on. Then I'm checking if it's a leaf. If it's a leaf, I just return the list, because that means the path has ended. Otherwise, it appends the result and continues down the tree.
The first issue is if t.is_leaf:
You are evaluating the Truthy-ness of a function. The function is defined, so it's Truthy.
To actually test the result of the function call, use: if t.is_leaf():
The second issue is that you are creating lists of lists:
Using return result + [preorder(b) for b in t.branches] the result is [1, [2], [3, [4], [5]], [6, [7]]]
Instead, you need to unwrap those sub-lists using, for example, a nested loop:
return result + [value for b in t.branches for value in preorder(b)]
I dont understand how changed type from list to dict on constructor in main class if i got to subclass another type - dict.
What is magic is this?
class Adder:
def __init__(self, data = [ ]):
self.data = data
def __add__(self, other):
return "Not Implemented"
class DictAdder(Adder):
def __add__(self, y):
z = {}
for k in self.data.keys(): z[k] = self.data[k]
for k in y.keys(): z[k] = y[k]
return z
y = DictAdder({1:'a', 2:'b'})
l = y + { 3: 'c', 4: 'd'}
print(l)
and answer:
{1: 'a', 2: 'b', 3: 'c', 4: 'd'}
Pycharm show what self.data.keys() dont have attribute keys() and this must be try, because data is a list, but this is work
data defaults to a list, but Python isn't a statically typed language so it doesn't have to be. data is just a name. Just because we have this:
class Adder:
def __init__(self, data = [ ]):
self.data = data
Doesn't limit our usage. I can still do:
i = Adder(4)
s = Adder('hello')
d = Adder({1: 2})
o = Adder(SomeOtherType())
As long as whatever uses Adder.data uses it correctly for the type it happens to be. Since in this case, DictAdder interprets data as a dict, and it is, that works fine. Of course, if you tried to use a DictAdder constructed with a non-dict, you would get an error.
I need to operate on two separate infinite list of numbers, but could not find a way to generate, store and operate on it in python.
Can any one please suggest me a way to handle infinite Arithmetic Progession or any series and how to operate on them considering the fact the minimal use of memory and time.
Thanks every one for their suggestions in advance.
You are looking for a python generator instead:
def infinitenumbers():
count = 0
while True:
yield count
count += 1
The itertools package comes with a pre-built count generator.
>>> import itertools
>>> c = itertools.count()
>>> next(c)
0
>>> next(c)
1
>>> for i in itertools.islice(c, 5):
... print i
...
2
3
4
5
6
This is where the iterator comes in. You can't have an infinite list of numbers, but you can have an infinite iterator.
import itertools
arithmetic_progression = itertools.count(start,step) #from the python docs
The docs for Python2 can be found here
I have another python3 solution (read SICP chapter 3.5)
class Stream:
def __init__(self, head, tail):
self.head = head
self.tail = tail
self.memory = None
self.isDone = False
def car(self):
return self.head
def cdr(self):
if self.isDone:
return self.memory
self.memory = self.tail()
self.isDone = True
return self.memory
def __getitem__(self, pullFrom):
if pullFrom < 1 or self.memory == []:
return []
return [self.car()] + self.cdr()[pullFrom - 1]
def __repr__(self):
return "[" + repr(self.car()) + " x " + repr(self.tail) + "]"
def map(self, func):
if self.memory == []:
return []
return Stream(func(self.car()), lambda: Stream.map(self.cdr(), func))
def from_list(lst):
if lst == []:
return []
return Stream(lst[0], lambda:
Stream.from_list(lst[1:]))
def filter(self, pred):
if self.memory == []:
return []
elif pred(self.car()):
return Stream(self.car(), lambda: Stream.filter(self.cdr(), pred))
else:
return self.cdr().filter(pred)
def sieve(self):
return Stream(self.car(), lambda: self.cdr().filter(lambda n: n % self.car() > 0).sieve())
def foreach(self, action, pull = None):
if pull is None:
action(self.car())
self.cdr().foreach(action, pull)
elif pull <= 0:
return
else:
action(self.car())
self.cdr().foreach(action, pull-1)and run:
a = Stream(0, lambda: a.map((lambda x: x + 1)))
print(a[10])
which returns:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] .
But streams are lazily evaluated, so:
>>> a = Stream(0, lambda: a.map((lambda x: x + 1)))
>>> print(a)
prints:
[0 x [...]]
To create an object that acts like a "mutable" infinite list, you can overload the __getitem__ and __setitem__ methods in a class:
class infinite_list():
def __init__(self, func):
self.func = func
self.assigned_items = {}
def __getitem__(self, key):
if key in self.assigned_items:
return self.assigned_items[key]
else:
return self.func(key)
def __setitem__(self, key , value):
self.assigned_items[key] = value
Then, you can initialize the "infinite list" with a lambda expression and modify an item in the list:
infinite_thing = infinite_list(lambda a: a*2)
print(infinite_thing[1]) #prints "2"
infinite_thing[1] = infinite_thing[2]
print(infinite_thing[1]) #prints "4"
Similarly, it is possible to create an "infinite dictionary" that provides a default value for each missing key.
Perhaps the natural way to generate an infinite series is using a generator:
def arith(a, d):
while True:
yield a
a += d
This can be used like so:
print list(itertools.islice(arith(10, 2), 100))
My solution is:
from hofs import *
def cons_stream(head,tail):
return [head,tail,False,False]
def stream_cdr(strm):
if strm[2]:
return strm[3]
strm[3] = strm[1]()
strm[2] = True
return strm[3]
def show_stream(stream, num = 10):
if empty(stream):
return []
if num == 0:
return []
return adjoin(stream[0], show_stream(stream_cdr(stream), num - 1))
def add_streams(a , b):
if empty(a):
return b
if empty(b):
return a
return cons_stream(a[0] + b[0] , lambda : add_streams( stream_cdr(a), stream_cdr(b)))
def stream_filter( pred , stream ):
if empty(stream):
return []
if pred(stream[0]):
return cons_stream(stream[0], lambda : stream_filter(pred, stream_cdr(stream)))
else:
return stream_filter( pred , stream_cdr( stream ))
def sieve(stream):
return cons_stream(stream[0] , lambda : sieve(stream_filter(lambda x : x % stream[0] > 0 , stream_cdr(stream))))
ones = cons_stream(1, lambda : ones)
integers = cons_stream(1, lambda : add_streams(ones, integers))
primes = sieve(stream_cdr(integers))
print(show_stream(primes))
Copy the Python code above.
When I tried it, i got [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] which is 10 of an infinite list of primes.
You need hofs.py to be
def empty(data):
return data == []
def adjoin(value,data):
result = [value]
result.extend(data)
return result
def map(func, data):
if empty(data):
return []
else:
return adjoin(func(data[0]), map(func, data[1:]))
def keep(pred, data):
if empty(data):
return []
elif pred(data[0]):
return adjoin( data[0] , keep(pred, data[1:]))
else:
return keep(pred, data[1:])
I assume you want a list of infinite numbers within a range. I have a similar problem, and here is my solution:
c = 0
step = 0.0001 # the difference between the numbers
limit = 100 # The upper limit
myInfList = []
while c <= limit:
myInfList.append(c)
c = c + step
print(myInfList)