How work unpacking in python? [duplicate] - python

Is it possible to simulate extended tuple unpacking in Python 2?
Specifically, I have a for loop:
for a, b, c in mylist:
which works fine when mylist is a list of tuples of size three. I want the same for loop to work if I pass in a list of size four.
I think I will end up using named tuples, but I was wondering if there is an easy way to write:
for a, b, c, *d in mylist:
so that d eats up any extra members.

You can't do that directly, but it isn't terribly difficult to write a utility function to do this:
>>> def unpack_list(a, b, c, *d):
... return a, b, c, d
...
>>> unpack_list(*range(100))
(0, 1, 2, (3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99))
You could apply it to your for loop like this:
for sub_list in mylist:
a, b, c, d = unpack_list(*sub_list)

You could define a wrapper function that converts your list to a four tuple. For example:
def wrapper(thelist):
for item in thelist:
yield(item[0], item[1], item[2], item[3:])
mylist = [(1,2,3,4), (5,6,7,8)]
for a, b, c, d in wrapper(mylist):
print a, b, c, d
The code prints:
1 2 3 (4,)
5 6 7 (8,)

For the heck of it, generalized to unpack any number of elements:
lst = [(1, 2, 3, 4, 5), (6, 7, 8), (9, 10, 11, 12)]
def unpack(seq, n=2):
for row in seq:
yield [e for e in row[:n]] + [row[n:]]
for a, rest in unpack(lst, 1):
pass
for a, b, rest in unpack(lst, 2):
pass
for a, b, c, rest in unpack(lst, 3):
pass

You can write a very basic function that has exactly the same functionality as the python3 extended unpack. Slightly verbose for legibility. Note that 'rest' is the position of where the asterisk would be (starting with first position 1, not 0)
def extended_unpack(seq, n=3, rest=3):
res = []; cur = 0
lrest = len(seq) - (n - 1) # length of 'rest' of sequence
while (cur < len(seq)):
if (cur != rest): # if I am not where I should leave the rest
res.append(seq[cur]) # append current element to result
else: # if I need to leave the rest
res.append(seq[cur : lrest + cur]) # leave the rest
cur = cur + lrest - 1 # current index movded to include rest
cur = cur + 1 # update current position
return(res)

Python 3 solution for those that landed here via an web search:
You can use itertools.zip_longest, like this:
from itertools import zip_longest
max_params = 4
lst = [1, 2, 3, 4]
a, b, c, d = next(zip(*zip_longest(lst, range(max_params))))
print(f'{a}, {b}, {c}, {d}') # 1, 2, 3, 4
lst = [1, 2, 3]
a, b, c, d = next(zip(*zip_longest(lst, range(max_params))))
print(f'{a}, {b}, {c}, {d}') # 1, 2, 3, None
For Python 2.x you can follow this answer.

Related

in python language create several variables in a loop and after append items in each ones

I'm trying to find a way to append items in variables created on the fly
c = ('a','b','g','d','j')
p = 2
for r in c:
globals()['ssvar%s' % r] = []
for z in range (0,10,1):
for r in c:
p = p + 2
(['ssvar%s' % r]).append (p)
print ssvara #result >>> []
print ssvarb #result >>> []
print ssvarg #result >>> []
print ssvard #result >>> []
print ssvarj #result >>> []
but the expression (['ssvar%s' % poire]).append doesn't work.
can you direct me to the same topic or tell me how to vary the variable name to be fill ?
Don't do this, but I think what you were looking to do is
c = ('a','b','g','d','j')
p = 2
for r in c:
globals()['ssvar%s' % r] = []
for z in range (0, 10, 1):
for r in c:
p = p + 2
globals()['ssvar%s' % r].append(p)
Instead, you can create your own dictionary (container of key: value pairs) and store the lists in there as values and use the keys as names. If this dictionary is called my_dict, then my_dict['ssvara'] references the list corresponding to 'ssvara', my_dict['ssvarb'] references the list corresponding to 'ssvarb' and so on.
c = ('a','b','g','d','j')
p = 2
my_dict = {}
for r in c:
my_dict['ssvar%s' % r] = []
for z in range (0, 10, 1):
for r in c:
p = p + 2
my_dict['ssvar%s' % r].append(p)
print my_dict
Output
{'ssvara': [4, 14, 24, 34, 44, 54, 64, 74, 84, 94],
'ssvarb': [6, 16, 26, 36, 46, 56, 66, 76, 86, 96],
'ssvard': [10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
'ssvarg': [8, 18, 28, 38, 48, 58, 68, 78, 88, 98],
'ssvarj': [12, 22, 32, 42, 52, 62, 72, 82, 92, 102]}
If the actual names are not important (you are, after all, creating them dynamically), you can just create a list of lists. If this list is called my_list, my_list[0] references the first sublist, my_list[1] references the second, and so on.
c = ('a','b','g','d','j')
p = 2
my_list = [[] for i in range(len(c))]
for z in range (0, 10, 1):
for l in my_list:
p = p + 2
l.append(p)
print my_list
Output
[[4, 14, 24, 34, 44, 54, 64, 74, 84, 94],
[6, 16, 26, 36, 46, 56, 66, 76, 86, 96],
[8, 18, 28, 38, 48, 58, 68, 78, 88, 98],
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
[12, 22, 32, 42, 52, 62, 72, 82, 92, 102]]
I don't use Python 2 so I had to make a few interpolations.
I try to create temporaly variables with multiple items inside.
in fact I need to have, for exemple, this result:
ssvara
>>>> ['4','6','8','10','12','14','16', '8','20','22']
ssvara
>>>> ['24','26','28','30','32','34','36', '38','40','42']
the value of p is not important, the main thing is to be able to append. even with a local variable.

Cycle through lists based on different list?

I want to cycle through my first list c based on my second list changes.
What I would like to see as a result is:
60, 71, 62, 69, 64, 71, 64, 67.
Currently, it just prints the list c. My real list for changes has 64 numbers in total.
For context the list c is the midi values for the C maj scale.
How can I achieve this? I'm sure it's simple.
C = [60, 62, 64, 65, 67, 69, 71]
changes = [0, 6, 2, 4, 4, 4, 3, 2]
for notes in C:
print(notes)
Try:
C = [60, 62, 64, 65, 67, 69, 71]
changes = [0, 6, 2, 4, 4, 4, 3, 2]
output = []
idx_current = 0
for x in changes:
idx_current = (idx_current + x) % len(C)
output.append(C[idx_current])
print(output) # [60, 71, 62, 69, 64, 71, 64, 67]
Based on your output, I guess you want to increase the index of C while wrapping around if necessary. This is done in the line idx = (idx + d) % len(C), i.e., increase the index modulo len(C).
Alternatively, using Assignment expression for Python 3.8+, you can do (with the same idea)
idx_cur = 0
idx = [idx_cur := (idx_cur + x) % len(C) for x in changes]
output = [C[i] for i in idx]

How to get all values possible from a list of number without itertools

I want to create an algorithm that find all values that can be created with the 4 basic operations + - * / from a list of number n, where 2 <= len(l) <= 6 and n >= 1
All numbers must be integers.
I have seen a lot of similar topics but I don't want to use the itertool method, I want to understand why my recursive program doesn't work
I tried to make a costly recursive program that makes an exhaustive search of all the possible combinations, like a tree with n=len(l) start and each tree depth is n.
L list of the starting number
C the current value
M the list of all possible values
My code:
def result(L,C,M):
if len(L)>0:
for i in range(len(L)) :
a=L[i]
if C>=a:
l=deepcopy(L)
l.remove(a)
m=[] # new current values
#+
m.append(C+a)
# * 1 is useless
if C !=1 or a !=1:
m.append(C*a)
# must be integer
if C%a==0 and a<=C: # a can't be ==0
m.append(C//a)
#0 is useless
if C!=a:
m.append(C-a)
for r in m: #update all values possible
if r not in M:
M.append(r)
for r in m: # call the fucntion again with new current values,and updated list of remaining number
result(l,r,M)
def values_possible(L) :
m=[]
for i in L:
l=deepcopy(L)
l.remove(i)
result(l,i,m)
m.sort()
return m
For small lists without duplicate numbers, my algorithm seems to work but with lists like [1,1,2,2,4,5] it misses some values.
It returns:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 94, 95, 96, 97, 98, 99, 100, 101,
102, 104, 105, 110, 112, 115, 116, 118, 119, 120, 121, 122, 124, 125, 128, 130,
140, 160]
but it misses 93,108,114,117,123,126,132,135,150,180.
Let's take an even simpler example: [1, 1, 2, 2].
One of the numbers your algorithm can't find is 9 = (1 + 2) * (1 + 2).
Your algorithm simply cannot come up with this computation because it always deals with a "current" value C. You can start with C = 1 + 2, but you cannot find the next 1 + 2 because it has to be constructed separately.
So your recursion will have to do at least some kind of partitioning into two groups, finding all the answers for those and then doing combining them.
Something like this could work:
def partitions(L):
if not L:
yield ([], [])
else:
for l, r in partitions(L[1:]):
yield [L[0]] + l, r
yield l, [L[0]] + r
def values_possible(L):
if len(L) == 1:
return L
results = set()
for a, b in partitions(L):
if not a or not b:
continue
for va in values_possible(a):
for vb in values_possible(b):
results.add(va + vb)
results.add(va * vb)
if va > vb:
results.add(va - vb)
if va % vb == 0:
results.add(va // vb)
return results
Not too efficient though.

how to split a list into multiple lists with certain ranges?

I have a list that needs to be split into 4 separate lists with maximum size of 21 depending on the amount of items in the original list.
The master list can have from 1 to 84 items.
I want the items to start in a and fill up to a maximum of 21 in a, b, c, d
I have the following code that can split the items up no problem but I want to know if there is a shorter way to do this. I am repeating code a lot except the range.
codes = [x for x in range(80)] # range anywhere between 1-84
print(len(codes))
a = []
b = []
c = []
d = []
for i in range(0, 21):
try:
a.append(codes[i])
except IndexError:
pass
for i in range(21, 42):
try:
b.append(codes[i])
except IndexError:
pass
for i in range(42, 63):
try:
c.append(codes[i])
except IndexError:
pass
for i in range(63, 84):
try:
d.append(codes[i])
except IndexError:
pass
print(len(a), len(b), len(c), len(d))
print(a)
print(b)
print(c)
print(d)
Before that I had this code that works great for the whole 84 items as the order is not important..
a = []
b = []
c = []
d = []
for a1, b1, c1, d1 in zip(*[iter(codes)]*4):
a.append(a1)
b.append(b1)
c.append(c1)
d.append(d1)
However if i have say 4 items, it will add 1 to each
a = [0]
b = [1]
c = [2]
d = [3]
What I would like to obtain is
a = [0, 1, 2, 3]
b = []
c = []
d = []
You can simply use sublist
a = codes[0: 21]
b = codes[21:42]
c = codes[42:63]
d = codes[63:84]
This will be fine for your requirement
#SajalPreetSinghs answer is correct for a simple use case like OP's but it also has some disadvantages when it comes to scalability.
For example:
When you need 20 sublists instead of the actual 4. You would have to add 16 more lines and specify 2 different numbers per line each!
Now imagine you already extended the code to 20 sublists but now you want the maximum item count per sublist to be 37 instead of 21 - you would have to change 2*20 = 40 numbers!
Improved scalability with generators
So if you want something with a better scalability you could use the following code which makes usage of generators:
Code
def sublist(orig_list, list_of_subs, max_items_per_list):
def sublist_generator():
for sublist in list_of_subs:
yield sublist
sublist = sublist_generator()
current_sublist = next(sublist)
for element in orig_list:
current_sublist.append(element)
if len(current_sublist) == max_items_per_list: # current list is full
current_sublist = next(sublist) # so let current point to the next list
Setup and usage
import random
start = 1
stop = random.randint(2, 85) # generate random int inclusively 2 and 85
codes = [x for x in range(start, stop)] # stop is exclusive: range(1, 85) == [1, 2, ..., 84]
a, b, c, d = [], [], [], []
sublists = [a, b, c, d] # *1
sublist(codes, sublists, 21)
for sublist in sublists:
print(sublist)
Better scalability because
If you want to change the number of items per sublist you only have to pass in the new maximum number.
If you want to increase the number of sublists you only have to add more of them to the sublists variable which you pass to the function (see *1).
If you need this code more often it's no problem because you can comfortably call the function.
I hope this helps someone!
Cheers
winklerrr
You could use the zip_longest function from itertools
from itertools import zip_longest
master_list = range(0, 84)
iterables = [iter(master_list)] * 21
slices = zip_longest(*iterables, fillvalue=None)
for slice in slices:
print("slice", slice)
# slice (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)
# slice (21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41)
# slice (42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62)
# slice (63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83)
You can do it with using a list comprehension:
MAX_SIZE = 21
l = list(range(80))
l1,l2,l3,l4 = [l[i*MAX_SIZE: (i+1)*MAX_SIZE] for i in range(4)]
#l1=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,20],
# ....
#l4=[63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79]
What I would do would be just put it in one loop.
if index is 0 to 83
for i in range(0,84):
if i>=0 and i<=20:
a.append(codes[i])
elif i>20 and i<=41:
b.append(codes[i])
elif i>41 and i<=62:
c.append(codes[i])
elif i>62 and i<=83:
d.append(codes[i])
A solution using a list comprehension:
i_list = range(0,84)
r = 21
res = [i_list[i:i+r] for i in range(0,len(i_list),r)]

Remove elements from array updating list of stored indexes accordingly

Consider a numpy array of the form:
> a = np.random.uniform(0., 100., (10, 1000))
and a list of indexes to elements in that array that I want to keep track of:
> idx_s = [0, 5, 7, 9, 12, 17, 19, 32, 33, 35, 36, 39, 40, 41, 42, 45, 47, 51, 53, 57, 59, 60, 61, 62, 63, 65, 66, 70, 71, 73, 75, 81, 83, 85, 87, 88, 89, 90, 91, 93, 94, 96, 98, 100, 106, 107, 108, 118, 119, 121, 124, 126, 127, 128, 129, 133, 135, 138, 142, 143, 144, 146, 147, 150]
I also have a list of indexes of elements I need to remove from a:
> idx_d = [4, 12, 18, 20, 21, 22, 26, 28, 29, 31, 37, 43, 48, 54, 58, 74, 80, 86, 99, 109, 110, 113, 117, 134, 139, 140, 141, 148, 154, 156, 160, 166, 169, 175, 183, 194, 198, 199, 219, 220, 237, 239, 241, 250]
which I delete with:
> a_d = np.delete(arr, idx_d, axis=1)
But this process alters the indexes of elements in a_d. The indexes in idx_s no longer point in a_d to the same elements in a, since np.delete() moved them. For example: if I delete the element of index 4 from a, then all indexes after 4 in idx_s are now displaced by 1 to the right in a_d.
v Index 5 points to 'f' in a
0 1 2 3 4 5 6
a -> a b c d e f g ... # Remove 4th element 'e' from a
a_d -> a b c d f g h ... # Now index 5 no longer points to 'f' in a_d, but to 'g'
0 1 2 3 4 5 6
How do I update the idx_s list of indexes, so that the same elements that were pointed in a are pointed in a_d?
In the case of an element that is present in idx_s that is also present in idx_d (and thus removed from a and not present in a_d) its index should also be discarded.
You could use np.searchsorted to get the shifts for each element in idx_s and then simply subtract those from idx_s for the new shifted-down values, like so -
idx_s - np.searchsorted(idx_d, idx_s)
If idx_d is not already sorted, we need to feed in a sorted version. Thus, for simplicity assuming these as arrays, we would have -
idx_s = idx_s[~np.in1d(idx_s, idx_d)]
out = idx_s - np.searchsorted(np.sort(idx_d), idx_s)
A sample run to help out getting a better picture -
In [530]: idx_s
Out[530]: array([19, 5, 17, 9, 12, 7, 0])
In [531]: idx_d
Out[531]: array([12, 4, 18])
In [532]: idx_s = idx_s[~np.in1d(idx_s, idx_d)] # Remove matching ones
In [533]: idx_s
Out[533]: array([19, 5, 17, 9, 7, 0])
In [534]: idx_s - np.searchsorted(np.sort(idx_d), idx_s) # Updated idx_s
Out[534]: array([16, 4, 15, 8, 6, 0])
idx_s = [0, 5, 7, 9, 12, 17, 19]
idx_d = [4, 12, 18]
def worker(a, v, i=0):
if not a:
return []
elif not v:
return []
elif a[0] == v[0]:
return worker(a[1:], v[1:], i+1)
elif a[0] < v[0]:
return [a[0]-i] + worker(a[1:], v, i)
else:
return [a[0]-i-1] + worker(a[1:], v[1:], i+1)
worker(idx_s, idx_d)
# [0, 5, 6, 8, 15, 16]

Categories

Resources