Python casting leads to weird mutability result - python

Why is it that when I write the code block as:
def rev(li):
li.reverse()
l = [[1, 2, 3], [4, 5, 6]]
map(rev, l)
print(l)
The resulting list is the same as the original list, but when I rewrite the code to now cast the map object into a list as such:
def rev(li):
li.reverse()
l = [[1, 2, 3], [4, 5, 6]]
list(map(rev, l))
print(l)
It does reverse the inner lists to give me [[3, 2, 1], [6, 5, 4]]. How does casting the map object to a list change mutability rules for the list l?

Related

Quicker way to join multiple lists passed as args to a Python function?

So I have a function which takes a variable number of lists as an argument, then combines those lists into one single list:
def comb_lists(*lists):
sublist = []
for l in lists:
sublist.extend(l)
print(sublist)
>>> comb_lists([1, 2], [3, 4], [5, 6])
[1, 2, 3, 4, 5, 6]
And it works. But I was just wondering if there was a simpler solution? I tried a list comprehension using list unpacking, but that returned a SyntaxError:
def comb_lists(*lists):
sublist = [*l for l in lists]
>>> comb_lists([1, 2], [3, 4], [5, 6])
SyntaxError: iterable unpacking cannot be used in comprehension
Is there any neater or quicker way to do this?
EDIT: itertools looks really useful for this sort of thing. I'd be interested to know if there's any way of doing it that doesn't rely on imports though.
here is the simplest solution
result = sum(lists, [])
There's built-in function chain.form_iterable() in itertools module to do this:
>>> from itertools import chain
>>> my_list = [[1, 2], [3, 4], [5, 6]]
>>> list(chain.from_iterable(my_list))
[1, 2, 3, 4, 5, 6]
If you do not want to import any module, you can write nested list comprehension to achieve this as:
>>> my_list = [[1, 2], [3, 4], [5, 6]]
>>> [e for l in my_list for e in l]
[1, 2, 3, 4, 5, 6]

How to do Math Functions on Lists within a List

I'm very new to python (using python3) and I'm trying to add numbers from one list to another list. The only problem is that the second list is a list of lists. For example:
[[1, 2, 3], [4, 5, 6]]
What I want is to, say, add 1 to each item in the first list and 2 to each item in the second, returning something like this:
[[2, 3, 4], [6, 7, 8]]
I tried this:
original_lst = [[1, 2, 3], [4, 5, 6]]
trasposition_lst = [1, 2]
new_lst = [x+y for x,y in zip(original_lst, transposition_ls)]
print(new_lst)
When I do this, I get an error
can only concatenate list (not "int") to list
This leads me to believe that I can't operate in this way on the lists as long as they are nested within another list. I want to do this operation without flattening the nested list. Is there a solution?
One approach using enumerate
Demo:
l = [[1, 2, 3], [4, 5, 6]]
print( [[j+i for j in v] for i,v in enumerate(l, 1)] )
Output:
[[2, 3, 4], [6, 7, 8]]
You can use enumerate:
l = [[1, 2, 3], [4, 5, 6]]
new_l = [[c+i for c in a] for i, a in enumerate(l, 1)]
Output:
[[2, 3, 4], [6, 7, 8]]
Why don't use numpy instead?
import numpy as np
mat = np.array([[1, 2, 3], [4, 5, 6]])
mul = np.array([1,2])
m = np.ones(mat.shape)
res = (m.T *mul).T + mat
You were very close with you original method. Just fell one step short.
Small addition
original_lst = [[1, 2, 3], [4, 5, 6]]
transposition_lst = [1, 2]
new_lst = [[xx + y for xx in x] for x, y in zip(original_lst, transposition_lst)]
print(new_lst)
Output
[[2, 3, 4], [6, 7, 8]]
Reasoning
If you print your original zip it is easy to see the issue. Your original zip yielded this:
In:
original_lst = [[1, 2, 3], [4, 5, 6]]
transposition_lst = [1, 2]
for x,y in zip(original_lst, transposition_lst):
print(x, y)
Output
[1, 2, 3] 1
[4, 5, 6] 2
Now it is easy to see that you are trying to add an integer to a list (hence the error). Which python doesn't understand. if they were both integers it would add them or if they were both lists it would combine them.
To fix this you need to do one extra step with your code to add the integer to each value in the list. Hence the addition of the extra list comprehension in the solution above.
A different approach than numpy that could work even for lists of different lengths is
lst = [[1, 2, 3], [4, 5, 6, 7]]
c = [1, 2]
res = [[l + c[i] for l in lst[i]] for i in range(len(c))]

In the for-loop, when I use 'append' to add a new element to the list, the one that has added before also change

I want to get transpose of matrix B without using Numpy. When I use 'append' to add a new element to the list, the one that has added before also change. How can I fix it?
from decimal import *
B = [[1,2,3,5],
[2,3,3,5],
[1,2,5,1]]
def shape(M):
r = len(M)
c = len(M[0])
return r,c
def matxRound(M, decPts=4):
for p in M:
for index in range(len(M[0])):
p[index] = round(p[index], decPts)
def transpose(M):
c_trans, r_trans = shape(M)
new_row = [0]*c_trans
trans_M = []
for i in range(r_trans):
for j in range(c_trans):
new_row[j] = M[j][i]
print 'new_row',new_row
print 'trans_M before append',trans_M
trans_M.append(new_row)
print 'trans_M after append',trans_M
return trans_M
print transpose(B)
The output is here:
new_row [1, 2, 1]
trans_M before append []
trans_M after append [[1, 2, 1]]
new_row [2, 3, 2]
trans_M before append [[2, 3, 2]]
trans_M after append [[2, 3, 2], [2, 3, 2]]
new_row [3, 3, 5]
trans_M before append [[3, 3, 5], [3, 3, 5]]
trans_M after append [[3, 3, 5], [3, 3, 5], [3, 3, 5]]
new_row [5, 5, 1]
trans_M before append [[5, 5, 1], [5, 5, 1], [5, 5, 1]]
trans_M after append [[5, 5, 1], [5, 5, 1], [5, 5, 1], [5, 5, 1]]
[[5, 5, 1], [5, 5, 1], [5, 5, 1], [5, 5, 1]]
I will complete #glibdud comment's answer :
What you are doing now is creating a list that fits your needs for your Transpose.
You are creating your new matrix.
You are, then, appending your transposed value into your new matrix... without creating a new Transpose list.
What happens then is that you modify the last list you just appended, and try to append it again.
So in the end, you added the 4 same lists to your new matrix. As the 4 lists point to the same address in memory as they are the same object, your new matrix have 4 identical rows.
The most pythonic way I know to perform matrix transposition without using Numpy (that should be the preferred way), is by using list unpacking (list expansion) and the builtin zip function transposed = list(zip(*B)).
However, zip() return tuples while your original matrix is a list of lists. So, if you want to keep your structure, you can use transposed = [list(i) for i in zip(*B)]

Printing a column of a 2-D List in Python

Suppose if A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Then A[0][:] prints [1, 2, 3]
But why does A[:][0] print [1, 2, 3] again ?
It should print the column [1, 4, 7], shouldn't it?
[:] is equivalent to copy.
A[:][0] is the first row of a copy of A.
A[0][:] is a copy of the first row of A.
The two are the same.
To get the first column: [a[0] for a in A]
Or use numpy and np.array(A)[:,0]
When you don't specify a start or end index Python returns the entire array:
A[:] = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[:] matches the entire list.
So A[:] is the same as A. So A[0][:] is the same as A[0].
And A[0][:] is the same as A[0].
A[:] returns a copy of the entire list. which is A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
A[:][0] Thus selects [1, 2, 3].
If you want the first column, do a loop:
col = []
for row in A:
col.append(row[0])
A is actually a list of list, not a matrix. With A[:][0] You are accessing the first element (the list [1,2,3]) of the full slice of the list A. The [:] is Python slice notation (explained in the relevant Stack Overflow question).
To get [1,4,7] you would have to use something like [sublist[0] for sublist in A], which is a list comprehension, a vital element of the Python language.
Note that [:] just gives you a copy of all the content of the list. So what you are getting is perfectly normal. I think you wanted to use this operator as you would in numpy or Matlab. This does not do the same in regular Python.
A[0] is [1, 2, 3]
Therefore A[0][:] is also [1, 2, 3]
A[:] is [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Therefore A[:][0] is [1, 2, 3]
If you wanted the first column you should try:
[e[0] for e in A]
# [1, 4, 7]
Problem
A is not a 2-D list: it is a list of lists. In consideration of that:
A[0] is the first list in A:
>>> A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> A[0]
[1, 2, 3]
Consequently, A[0][:]: is every element of the first list:
>>> A[0][:]
[1, 2, 3]
A[:] is every element of A, in other words it is a copy of A:
>>> A[:]
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Consequently, A[:][0] is the first element of that copy of A.
>>> A[:][0]
[1, 2, 3]
Solution
To get what you want, use numpy:
>>> import numpy as np
>>> A = np.array( [[1, 2, 3], [4, 5, 6], [7, 8, 9]] )
A is now a true two-dimensional array. We can get the first row of A:
>>> A[0,:]
array([1, 2, 3])
We can similarly get the first column of A:
>>> A[:,0]
array([1, 4, 7])
`

Convert nested iterables to list

Is there an easy way in python (using itertools, or otherwise) to convert a nested iterable f into its corresponding list or tuple? I'd like to save f so I can iterate over it multiple times, which means that if some nested elements of f are generators, I'll be in trouble.
I'll give an example input/output.
>>> g = iter(range(2))
>>> my_input = [1, [2, 3], ((4), 5), [6, g]]
>>> magical_function(my_input)
[1, [2, 3], [[4], 5], [6, [0, 1]]]
It would be fine if the output consisted of tuples, too. The issue is that iterating over g "consumes" it, so it can't be used again.
This seems like it would be best to do by checking if each element is iterable, and calling a recursive function over it if it is iterable. Just as a quick draw-up, I would try something like:
import collections
g = iter(range(2))
my_input = [1, [2, 3], ((4), 5), [6, g]]
def unfold(iterable):
ret = []
for element in iterable:
if isinstance(element, collections.Iterable):
ret.append(unfold(element))
else:
ret.append(element)
return ret
n = unfold(my_input)
print(n)
print(n)
which returns
$ python3 so.py
[1, [2, 3], [4, 5], [6, [0, 1]]]
[1, [2, 3], [4, 5], [6, [0, 1]]]
It's not the prettiest way, and you can find ways to improve it (it puts everything in a list instead of preserving tuples), but here is the general idea I would use.

Categories

Resources