Extend separate lists with the outputs of a function - python

I was wondering if there was a more elegant way of doing the following:
EDIT:
def whaa(x):
# Let's not get too picky about this function
return list(range(0,x)), list(range(-1,x))
a, b = whaa(10)
c = whaa(20)
a.extend(c[0])
b.extend(c[1])
EDIT: The behavior of the function is dependent on the input. And I want the corresponding outputs to go neatly into the same list.
Essentially, what I want to do is access the individual elements of the output tuple and extend my lists without going through the trouble of storing the output into a separate variable. It seems like given this construct, it's not something that's possible but I'm open to suggestions!

You can do it like this:
for x, y in zip([a, b], c):
x.extend(y)
But then why have you not just left a and b in a list in the first place?
c = whaa(10)
for x, y in zip(c, whaa(20)):
x.extend(y)
a, b = c # save unpacking until the end

Use a for loop to extend each element of the returned tuple:
a, b = tuple(x * 2 for x in whaa())
a
# [1, 2, 3, 1, 2, 3]
b
# [2, 3, 4, 2, 3, 4]
For the updated question, you can use zip as the answer of #John:
a, b = tuple(x + y for x, y in zip(whaa(10), whaa(20)))

Related

Python Function Returning List Objects Out of Order

I'm relatively new to the Python language. I'm aware of most of the basic functionality & theory, all the different classes of object and their behavior/properties etc.
Anyway, I was writing basic functions to explore different concepts in practice and get to know the language more intuitively. One in particular has left me vexed! Can anyone share any insight into why this result is not as expected?
Here is the code I ran:
test_list = [2, 4, 6]
def test_func(k):
global x, y, z
for n in k:
k[k.index(n)] = n * 2
x, y, z = k
return k
test_func(test_list)
print(test_list)
print(x)
print(y)
print(z)
I would have expected the result to be:
[4, 8, 12]
4
8
12
However, the actual result is as follows:
[8, 4, 12]
8
4
12
It seems that the first two items of the list have been swapped.
I can't see what could be causing this? If anyone can see what's happening here, please share the insight!
Thanks,
Oscar South
After first iteration you list look like [4,4,6] so k.index(4) return 0 index and multiple it by 2. So final result is [8,4,12].
I think you meant to do this:
test_list = [2, 4, 6]
def test_func(k):
global x, y, z
for i in range(len(k)):
k[i] = k[i] * 2
x, y, z = k
return k
You're mixing indexes with values, and using index() to find the position in an array is incorrect, most of all because you're modifying the list and you'll find the same elements again but in different positions, better use a range to iterate over the indexes and retrieve the values.
Also, using globals is not cool. In fact, the whole procedure should be written like a list comprehension instead - simple and idiomatic:
[2 * x for x in test_list]
=> [4, 8, 12]
In the first iteration the first element is changed to 4. In the second iteration the index of 4 is 0 not 1 as you expect.
Try this:
test_list = [2, 4, 6]
def test_func(k):
global x, y, z
l = list()
for n in k:
print(n, k.index(n))
l.append(n * 2)
x, y, z = l
return l
test_func(test_list)
print(test_list)
print(x)
print(y)
print(z)
You can condense the code into a list comprehension and unpacking:
test_list = [2, 4, 6]
def test_func(k):
global x, y, z
x, y, z = [i*2 for i in k]
return [x, y, z]

python passing by reference

Strange things happens today when I do things like this:
a = np.array([1, 2, 3])
b = a
b[0] = 0
print a, b
And then the value seems to be passed by reference! The answer becomes:
result: [0 2 3] [0 2 3]
But usually I think the variable in python is passed by value, like this:
a = np.array([1, 2, 3])
b = a
b = np.array([0, 2, 3])
print a, b
And then the answer becomes:
result: [1 2 3] [0 2 3]
But why is that happenning? How do I decide whether the variable is passed through reference or value? Some people said it was because of the mutable object, but I still don't quite get it. So can you explain it for me? Thanks a lot!
As it doesn't have anything directly to do with NumPy, let's rewrite it as:
a = [1, 2, 3]
b = a
b = [0, 2, 3]
print a, b
The key thing is that names and values are separate things. So on the first line:
a = [1, 2, 3]
You create a list and assign it to a
Now on the next line you assign a to b. There is still only 1 list, both names simply refer to it.
Next you create a new list and assign it to b. Now b refers to the new list instead of the old list. In turn now only 1 name (a) refers to the other list, instead of 2 names.
Thus if we take the first example, where you do:
b[0] = 0
There both a and b reference the same list, thus the change can be observed from both names, as both names refers to the same list.
Variable assignment in Python is always assignment to a reference. In other words, in Python, variables are names, and assigning a variable is declaring a name for something.
a = np.array([1, 2, 3])
b = a
b[0] = 0
print a, b
This means let a be the name for that first array, then let b be a name for a, but a means the first array, so really we're just saying let b also be a name for the first array. Then b[0] = 0 means change the first element of the array. Then when you print a and b, you are printing the same thing twice (the array), because a and b are names for the same thing.
a = np.array([1, 2, 3])
b = a
b = np.array([0, 2, 3])
print a, b
The first two lines are the same as last time. a is a name for the array and so is b. Then b = np.array([0, 2, 3]) means that b is now a name for this new array. So when you print a and b you are printing a, which is still the name for the first array, and b, which is the name for the second array.

To remove an element and assign the list in one line

So as the question says, is it possible to remove an element and return the list in one line?
So if lets say
a = [1, 3, 2, 4]
b = a.remove(1)
This would set b to be NoneType, so I was wondering if there's a way to do this.
I suppose this would work:
a = [1, 3, 2, 4]
b = (a.remove(1), a)[1]
This is assuming that you want to do both things:
Modify the original list by removing the element, and
Return the (now modified) list.
EDIT
Another alternative:
b = a.remove(1) or a
Both are fairly confusing; I'm not sure I would use either in code.
EDIT 2
Since you mentioned wanting to use this in a lambda... are you aware that you can use an actual named function any time you would otherwise use a lambda?
E.g. instead of something like this:
map(lambda x: x.remove(1) and x, foo)
You can do this:
def remove_and_return(x):
x.remove(1)
return x
map(remove_and_return, foo)
Since remove returns None, you could just or a to it:
>>> a = [1, 3, 2, 4]
>>> a.remove(1) or a
[3, 2, 4]
List a assign to b:
b = a.remove(1) or a
a is b # True
Using b is the same to use a, and it's dangerous if you get a final object like [b, c, d], because if you update one of this list element, the all elements will update too.
Compare to:
import copy
b = a.remove(1) or copy.copy(a)
I think this way should be safer.
pure functional solution that does not touch passed list
>>> a = [1,2,3]
>>> (lambda a,b: (lambda a0,a1: (a0.remove(b),a1)[-1] )( *((a.copy(),)*2) ))(a, 3)
[1,2]
>>> a
[1,2,3]

Extending lists directly from returned lists of functions

I want the output of returned values from a function to be extended to a set of lists.
def func():
return [-1,2],[4,1]
f=[1,2]
g=[3,4]
f,g=func() #How make it append
print f,g
#Expected Output:
[1,2,-1,2] [3,4,4,1]
For one list, I can do f.extend(func()) but what about multiple lists.
Creating temporary variables is not preferred.
Why not pass the function two parameters
def func(l1 ,l2):
return l1+[-1,2],l2+[4,1]
f = [1,2]
g = [3,4]
f,g = func(f,g)
print f,g
produces
[1, 2, -1, 2] [3, 4, 4, 1]
However, it may be easier to let the function extend the lists and avoid the return value altogether
def func(l1 ,l2):
l1.extend([-1,2])
l2.extend([4,1])
f = [1,2]
g = [3,4]
func(f,g)
print f,g
If this question is more general, and you cannot modify your function, use zip:
>>> f,g = (x+y for x,y in zip((f,g), func()))
>>> f
[1, 2, -1, 2]
>>> g
[3, 4, 4, 1]

python list comprehension unzipping multiple returns

anyone have any idea how to unpack the values in a tuple for a list comprehension?
So a practical example:
def func(x,y):
return x*2, y*2
x = [1, 2, 3]; y = [1, 2, 3]
a, b = [ func(i,j) for i, j in zip(x,y) ]
Unfortunately, that gives me an error sayin' there are too many values to unpack...
I've tried
zip(*func(i,j))
(a,b) = ...
Do you mean the following?:
a, b = zip(*[func(i,j) for i, j in zip(x,y)])
for x1,y1 in [func(i,j) for i, j in zip(x,y)]:
# do something with x1,y1
The problem is that the list comprehension returns something like
[(1,1), (4,4), (6,6),..]
so the list contains more than just two elements.
I don't see why you can't just do:
a = [i*2 for i in x]
b = [i*2 for i in y]
If you are worried about duplicate code, create a function:
def func(l):
return [i*2 for i in l]
a, b = func(x), func(y)
Trying to pack everything in one line, using fancy list unpacking etc., does not necessarily increase readability.

Categories

Resources