Reassigning a value to a list with a function - list

How would I reassign a value in a list every time i called it. For example in:
def randomfunction():
var1 = random.randint(1,10)
return var1
list1 = [None,None,None,None]
list1[1] = randomfunction()
How do I make the list[1] value equal to the randomfunction() rather than just one value returned by the function.
i.e everytime I call list[1] there is a new value from randomfunction().

To reassign the value, you just need to keep calling the function and you'll most likely receive a different number every time you call it.
For example:
import random
def randomfunction():
var1 = random.randint(1,10)
return var1
list1 = [None,None,None,None]
list1[1] = randomfunction()
print(list1)
list1[1] = randomfunction()
print(list1)
list1[1] = randomfunction()
print(list1)
I received an output of:
>>>
[None, 1, None, None]
[None, 6, None, None]
[None, 9, None, None]
So each time I called the function and assigned it to list1[1], I got a different value.

What do you think of this solution:
import random
class specli(list):
def __init__(self):
self.append(random.randint(1,10))
def __getitem__(self,i):
if i==0:
return random.randint(1,10)
elif i<len(self):
return super(specli,self).__getitem__(i)
else:
[][i]
L = specli()
print L
L.append('B')
print L
L.append('C')
print L
print '-------------'
print 'L[0] :',L[0]
print 'L[1] :',L[1]
print 'L[2] :',L[2]
print 'L[3] :',L[3]
result
[2]
[2, 'B']
[2, 'B', 'C']
-------------
L[0] : 2
L[1] : B
L[2] : C
L[3] :
Traceback (most recent call last):
File "I:\potoh\ProvPy\quichotte.py", line 24, in <module>
print 'L[3] :',L[3]
File "I:\potoh\ProvPy\quichotte.py", line 12, in __getitem__
[][i]
IndexError: list index out of range

Related

Magic method __repr__ leads to AttributeError with __new__ method

My goal is to give numpy.ndarray a different representation, since I want to represent some arrays with units. Thus, I programmed a class that inherits its attributes/ methods from numpy.ndarray. For the another representation I wanted to use the __repr__ magic method like:
class Quantitiy(np.ndarray):
def __new__(cls, value, unit=None, dtype=None, copy=True, order=None, subok=False, ndmin=0):
value = np.asarray(value)
obj = np.array(value, dtype=dtype, copy=copy, order=order,
subok=True, ndmin=ndmin).view(cls)
obj.__unit = util.def_unit(unit)
obj.__value = value
return obj
def __repr__(self):
prefix = '<{0} '.format(self.__class__.__name__)
sep = ','
arrstr = np.array2string(self.view(np.ndarray),
separator=sep,
prefix=prefix)
return '{0}{1} {2}>'.format(prefix, arrstr, self.__unit)
So far this works fine. However, if I want to access the inherited methods from numpy.ndarray I get a AttributeError because __repr__ cant resolve self.__unit.
I tried to solve this problem with a private method that defines the variable self.__unit and called it within the __new__ method but without success:
class Quantitiy(np.ndarray):
def __new__(cls, value, unit=None, dtype=None, copy=True, order=None, subok=False, ndmin=0):
value = np.asarray(value)
obj = np.array(value, dtype=dtype, copy=copy, order=order, subok=True, ndmin=ndmin).view(cls)
# Here I call the private method to initialize self.__unit.
obj.__set_unit()
obj.__value = value
return obj
def __repr__(self):
prefix = '<{0} '.format(self.__class__.__name__)
sep = ','
arrstr = np.array2string(self.view(np.ndarray), separator=sep, prefix=prefix)
return '{0}{1} {2}>'.format(prefix, arrstr, self.__unit)
# New defined private class.
def __set_unit(self, unit):
self.__unit = util.def_unit(unit)
I can not solve this with something like cls.__unit = util.def_unit(unit) in the __new__ method. I already tried to define a __init__ method after __new__. Moreover, I tried to interchange the private methods with public methods.
What I expect:
>>> array = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
>>> q = Quantity(value, unit="meter / second")
>>> q
<Quantitiy [[1,2,3,4],
[5,6,7,8]] meter/second>
>>> q * q
>>> <Quantitiy [[ 1, 4, 9,16],
[25,36,49,64]] meter**2/second**2>
>>> q.min()
>>> <Quantitiy 1 meter/second>
The actual result is:
>>> array = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
>>> q = Quantity(value, unit="meter / second")
>>> q
<Quantitiy [[1,2,3,4],
[5,6,7,8]] meter/second>
>>> q * q
>>> <Quantitiy [[ 1, 4, 9,16],
[25,36,49,64]] meter**2/second**2>
# Up to here everything works fine.
>>> q.min()
>>> AttributeError: 'Quantitiy' object has no attribute
'_Quantitiy__unit'
Does anyone see the mistake and can help me?
Ok, the answer is - as usual - in the FineManual (and could be found searching for "subclassing numpy ndarray" - which is how I found it actually), and requires implementing __array_finalize__(self, obj) :
import numpy as np
class Quantitiy(np.ndarray):
def __new__(cls, value, unit=None, dtype=None, copy=True, order=None, subok=False, ndmin=0):
value = np.asarray(value)
x = np.array(value, dtype=dtype, copy=copy, order=order, subok=True, ndmin=ndmin)
obj = x.view(type=cls)
obj._unit = unit
obj._value = value
return obj
def __repr__(self):
print("repr %s" % type(self))
prefix = '<{0} '.format(self.__class__.__name__)
sep = ','
arrstr = np.array2string(self.view(np.ndarray),
separator=sep,
prefix=prefix)
return '{0}{1} {2}>'.format(prefix, arrstr, self._unit)
def __array_finalize__(self, obj):
# see InfoArray.__array_finalize__ for comments
if obj is None:
return
self._unit = getattr(obj, '_unit', None)
self._value = getattr(obj, '_value', None)

Create a dictionary in a loop

I have 2 lists that I want to convert them into a dict with key and values. I managed to do so but there are too many steps so I would like to know if there's a simpler way of achieving this. Basically I would like to create the dict directly in the loop without having the extra steps bellow. I just started working with python and I don't quite understand all the datatypes that it provides.
The jName form can be modified if needed.
jName=["Nose", "Neck", "RShoulder", "RElbow", "RWrist", "LShoulder", "LElbow", "LWrist", "RHip",
"RKnee","RAnkle","LHip", "LKnee", "LAnkle", "REye", "LEye", "REar", "LEar"]
def get_joints(subset, candidate):
joints_per_skeleton = [[] for i in range(len(subset))]
# for each detected skeleton
for n in range(len(subset)):
# for each joint
for i in range(18):
cidx = subset[n][i]
if cidx != -1:
y = candidate[cidx.astype(int), 0]
x = candidate[cidx.astype(int), 1]
joints_per_skeleton[n].append((y, x))
else:
joints_per_skeleton[n].append(None)
return joints_per_skeleton
joints = get_joints(subset,candidate)
print joints
Here is the output of the joints list of list
[[None, (48.0, 52.0), (72.0, 50.0), None, None, (24.0, 55.0), (5.0, 105.0), None, (63.0, 159.0), (57.0, 221.0), (55.0, 281.0), (28.0, 154.0), (23.0, 219.0), (23.0, 285.0), None, (25.0, 17.0), (55.0, 18.0), (30.0, 21.0)]]
Here I defined a function to create the dictionary from the 2 lists
def create_dict(keys, values):
return dict(zip(keys, values))
my_dict = create_dict(jointsName, joints[0])
Here is the result:
{'LAnkle': (23.0, 285.0),
'LEar': (30.0, 21.0),
'LElbow': (5.0, 105.0),
'LEye': (25.0, 17.0),
'LHip': (28.0, 154.0),
'LKnee': (23.0, 219.0),
'LShoulder': (24.0, 55.0),
'LWrist': None,
'Neck': (48.0, 52.0),
'Nose': None,
'RAnkle': (55.0, 281.0),
'REar': (55.0, 18.0),
'RElbow': None,
'REye': None,
'RHip': (63.0, 159.0),
'RKnee': (57.0, 221.0),
'RShoulder': (72.0, 50.0),
'RWrist': None}
I think defaultdict could help you. I made my own example to show that you could predefine the keys and then go through a double for loop and have the values of the dict be lists of potentially different sizes. Please let me know if this answers your question:
from collections import defaultdict
import random
joint_names = ['hip','knee','wrist']
num_skeletons = 10
d = defaultdict(list)
for skeleton in range(num_skeletons):
for joint_name in joint_names:
r1 = random.randint(0,10)
r2 = random.randint(0,10)
if r1 > 4:
d[joint_name].append(r1*r2)
print d
Output:
defaultdict(<type 'list'>, {'hip': [0, 5, 30, 36, 56], 'knee': [35, 50, 10], 'wrist': [27, 5, 15, 64, 30]})
As a note I found it very difficult to read through your code since there were some variables that were defined before the snippet you posted.

adding item in the dictionary coming from a list resulted in TypeError

this is the code:
import bisect
data = {'sal': 25000} # stored data from user input
table = {1249.99: 36.30, 1749.99: 54.50, 2249.99: 72.70, 2749.99: 90.80,
3249.99: 109.00, 3749.99: 127.20, 4249.99: 145.30, 4749.99: 163.50,
5249.99: 181.70, 5749.99: 199.80, 6249.99: 218.00, 6749.99: 236.20,
7249.99: 254.30, 7749.99: 272.50, 8249.99: 290.70, 8749.99: 308.80,
9249.99: 327.00, 9749.99: 345.20, 10249.99: 363.30, 10749.99: 381.50,
11249.99: 399.70, 11749.99: 417.80, 12249.99: 436.00, 12749.99:
454.20, 13249.99: 472.30, 13749.99: 490.50, 14249.99: 508.70,
14749.99: 526.80, 15249.99: 545.00, 15749.99: 563.20, 15750.00:
581.30}
# get corresponding value from the table
table_bisect = bisect.bisect(sorted(table), data['sal'])
if table_bisect >= 30:
table_bisect = 30
else:
table_bisect = table_bisect
s_table = sorted(table.value())
data['new'] = ''.join(s_table[table_bisect:(table_bisect+1)]
# TypeError: sequence item 0: expected string, float found
Everything works fine, until the last line, which return the error above. How can I fix the error or what are the work around?
It is because if you slice with list[index:index+1], it simply returns one value, which in this case is a float:
>>> y = [21, 4, 2, 5, 4, 3, 7, 9]
>>> y[5:6]
[3]
>>> ''.join(y[5:6])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: sequence item 0: expected string, int found
>>>
Instead, just do the following:
data['new'] = s_table[table_bisect:(table_bisect+1)][0]
Another option is to do this:
data['new'] = ''.join(str(s_table[table_bisect:(table_bisect+1)]))
the join() method expects string type if you need to use it.

call dict iterator function for each member in dict

I want to execute some function for each item from dictionary/List. I tried doing this but its not giving expected results. Here is the code.
class GroupDict(dict):
def iteritems(self):
# do some operation for every member in dict
print "in __iter__"
return super(GroupDict, self).iteritems()
a = GroupDict()
a['a'] = 0
a['b'] = 1
for key in a.iteritems():
print key
for this I am getting output
in __iter__
('a', 0)
('b', 1)
But expected is
in __iter__
('a', 0)
in __iter__
('b', 1)
I don't want to do operation inside the for loop but whatever I mention in the class should be executed for every member. Please help me with this.
iteritems() only gets called once, before the loop actually starts.
Let's see why. This loop:
for x in EXP:
do_something()
Roughly translates to:
it = iter(EXP)
while True:
try:
x = next(it)
except StopIteration:
break
do_something()
If you want to do something for every member, it is easier to do what you want by yielding from iteritems():
def iteritems(self):
for k, v in super(GroupDict, self).iteritems():
print "doing something with %s / %s" % (k,v)
yield k, v

Pythonic way to convert a list of integers into a string of comma-separated ranges

I have a list of integers which I need to parse into a string of ranges.
For example:
[0, 1, 2, 3] -> "0-3"
[0, 1, 2, 4, 8] -> "0-2,4,8"
And so on.
I'm still learning more pythonic ways of handling lists, and this one is a bit difficult for me. My latest thought was to create a list of lists which keeps track of paired numbers:
[ [0, 3], [4, 4], [5, 9], [20, 20] ]
I could then iterate across this structure, printing each sub-list as either a range, or a single value.
I don't like doing this in two iterations, but I can't seem to keep track of each number within each iteration. My thought would be to do something like this:
Here's my most recent attempt. It works, but I'm not fully satisfied; I keep thinking there's a more elegant solution which completely escapes me. The string-handling iteration isn't the nicest, I know -- it's pretty early in the morning for me :)
def createRangeString(zones):
rangeIdx = 0
ranges = [[zones[0], zones[0]]]
for zone in list(zones):
if ranges[rangeIdx][1] in (zone, zone-1):
ranges[rangeIdx][1] = zone
else:
ranges.append([zone, zone])
rangeIdx += 1
rangeStr = ""
for range in ranges:
if range[0] != range[1]:
rangeStr = "%s,%d-%d" % (rangeStr, range[0], range[1])
else:
rangeStr = "%s,%d" % (rangeStr, range[0])
return rangeStr[1:]
Is there a straightforward way I can merge this into a single iteration? What else could I do to make it more Pythonic?
>>> from itertools import count, groupby
>>> L=[1, 2, 3, 4, 6, 7, 8, 9, 12, 13, 19, 20, 22, 23, 40, 44]
>>> G=(list(x) for _,x in groupby(L, lambda x,c=count(): next(c)-x))
>>> print ",".join("-".join(map(str,(g[0],g[-1])[:len(g)])) for g in G)
1-4,6-9,12-13,19-20,22-23,40,44
The idea here is to pair each element with count(). Then the difference between the value and count() is constant for consecutive values. groupby() does the rest of the work
As Jeff suggests, an alternative to count() is to use enumerate(). This adds some extra cruft that needs to be stripped out in the print statement
G=(list(x) for _,x in groupby(enumerate(L), lambda (i,x):i-x))
print ",".join("-".join(map(str,(g[0][1],g[-1][1])[:len(g)])) for g in G)
Update: for the sample list given here, the version with enumerate runs about 5% slower than the version using count() on my computer
Whether this is pythonic is up for debate. But it is very compact. The real meat is in the Rangify() function. There's still room for improvement if you want efficiency or Pythonism.
def CreateRangeString(zones):
#assuming sorted and distinct
deltas = [a-b for a, b in zip(zones[1:], zones[:-1])]
deltas.append(-1)
def Rangify((b, p), (z, d)):
if p is not None:
if d == 1: return (b, p)
b.append('%d-%d'%(p,z))
return (b, None)
else:
if d == 1: return (b, z)
b.append(str(z))
return (b, None)
return ','.join(reduce(Rangify, zip(zones, deltas), ([], None))[0])
To describe the parameters:
deltas is the distance to the next value (inspired from an answer here on SO)
Rangify() does the reduction on these parameters
b - base or accumulator
p - previous start range
z - zone number
d - delta
To concatenate strings you should use ','.join. This removes the 2nd loop.
def createRangeString(zones):
rangeIdx = 0
ranges = [[zones[0], zones[0]]]
for zone in list(zones):
if ranges[rangeIdx][1] in (zone, zone-1):
ranges[rangeIdx][1] = zone
else:
ranges.append([zone, zone])
rangeIdx += 1
return ','.join(
map(
lambda p: '%s-%s'%tuple(p) if p[0] != p[1] else str(p[0]),
ranges
)
)
Although I prefer a more generic approach:
from itertools import groupby
# auxiliary functor to allow groupby to compare by adjacent elements.
class cmp_to_groupby_key(object):
def __init__(self, f):
self.f = f
self.uninitialized = True
def __call__(self, newv):
if self.uninitialized or not self.f(self.oldv, newv):
self.curkey = newv
self.uninitialized = False
self.oldv = newv
return self.curkey
# returns the first and last element of an iterable with O(1) memory.
def first_and_last(iterable):
first = next(iterable)
last = first
for i in iterable:
last = i
return (first, last)
# convert groups into list of range strings
def create_range_string_from_groups(groups):
for _, g in groups:
first, last = first_and_last(g)
if first != last:
yield "{0}-{1}".format(first, last)
else:
yield str(first)
def create_range_string(zones):
groups = groupby(zones, cmp_to_groupby_key(lambda a,b: b-a<=1))
return ','.join(create_range_string_from_groups(groups))
assert create_range_string([0,1,2,3]) == '0-3'
assert create_range_string([0, 1, 2, 4, 8]) == '0-2,4,8'
assert create_range_string([1,2,3,4,6,7,8,9,12,13,19,20,22,22,22,23,40,44]) == '1-4,6-9,12-13,19-20,22-23,40,44'
This is more verbose, mainly because I have used generic functions that I have and that are minor variations of itertools functions and recipes:
from itertools import tee, izip_longest
def pairwise_longest(iterable):
"variation of pairwise in http://docs.python.org/library/itertools.html#recipes"
a, b = tee(iterable)
next(b, None)
return izip_longest(a, b)
def takeuntil(predicate, iterable):
"""returns all elements before and including the one for which the predicate is true
variation of http://docs.python.org/library/itertools.html#itertools.takewhile"""
for x in iterable:
yield x
if predicate(x):
break
def get_range(it):
"gets a range from a pairwise iterator"
rng = list(takeuntil(lambda (a,b): (b is None) or (b-a>1), it))
if rng:
b, e = rng[0][0], rng[-1][0]
return "%d-%d" % (b,e) if b != e else "%d" % b
def create_ranges(zones):
it = pairwise_longest(zones)
return ",".join(iter(lambda:get_range(it),None))
k=[0,1,2,4,5,7,9,12,13,14,15]
print create_ranges(k) #0-2,4-5,7,9,12-15
def createRangeString(zones):
"""Create a string with integer ranges in the format of '%d-%d'
>>> createRangeString([0, 1, 2, 4, 8])
"0-2,4,8"
>>> createRangeString([1,2,3,4,6,7,8,9,12,13,19,20,22,22,22,23,40,44])
"1-4,6-9,12-13,19-20,22-23,40,44"
"""
buffer = []
try:
st = ed = zones[0]
for i in zones[1:]:
delta = i - ed
if delta == 1: ed = i
elif not (delta == 0):
buffer.append((st, ed))
st = ed = i
else: buffer.append((st, ed))
except IndexError:
pass
return ','.join(
"%d" % st if st==ed else "%d-%d" % (st, ed)
for st, ed in buffer)
Here is my solution. You need to keep track of various pieces of information while you iterate through the list and create the result - this screams generator to me. So here goes:
def rangeStr(start, end):
'''convert two integers into a range start-end, or a single value if they are the same'''
return str(start) if start == end else "%s-%s" %(start, end)
def makeRange(seq):
'''take a sequence of ints and return a sequence
of strings with the ranges
'''
# make sure that seq is an iterator
seq = iter(seq)
start = seq.next()
current = start
for val in seq:
current += 1
if val != current:
yield rangeStr(start, current-1)
start = current = val
# make sure the last range is included in the output
yield rangeStr(start, current)
def stringifyRanges(seq):
return ','.join(makeRange(seq))
>>> l = [1,2,3, 7,8,9, 11, 20,21,22,23]
>>> l2 = [1,2,3, 7,8,9, 11, 20,21,22,23, 30]
>>> stringifyRanges(l)
'1-3,7-9,11,20-23'
>>> stringifyRanges(l2)
'1-3,7-9,11,20-23,30'
My version will work correctly if given an empty list, which I think some of the others will not.
>>> stringifyRanges( [] )
''
makeRanges will work on any iterator that returns integers and lazily returns a sequence of strings so can be used on infinite sequences.
edit: I have updated the code to handle single numbers that are not part of a range.
edit2: refactored out rangeStr to remove duplication.
how about this mess...
def rangefy(mylist):
mylist, mystr, start = mylist + [None], "", 0
for i, v in enumerate(mylist[:-1]):
if mylist[i+1] != v + 1:
mystr += ["%d,"%v,"%d-%d,"%(start,v)][start!=v]
start = mylist[i+1]
return mystr[:-1]