Passing a list while retaining the original - python

So I'm teaching myself Python, and I'm having an issue with lists. I want to pass my function a list and pop items off it while retaining the original list. How do I make python "instance" the passed list rather that passing a pointer to the original one?
Example:
def burninate(b):
c = []
for i in range(3):
c.append(b.pop())
return c
a = range(6)
d = burninate(a)
print a, d
Output: [0, 1, 2] [5, 4, 3]
Desired output: [0, 1, 2, 3, 4, 5] [5, 4, 3]
Thanks!

As other answers have suggested, you can provide your function with a copy of the list.
As an alternative, your function could take a copy of the argument:
def burninate(b):
c = []
b = list(b)
for i in range(3):
c.append(b.pop())
return c
Basically, you need to be clear in your mind (and in your documentation) whether your function will change its arguments. In my opinion, functions that return computed values should not change their arguments, and functions that change their arguments should not return anything. See python's [].sort(), [].extend(), {}.update(), etc. for examples. Obviously there are exceptions (like .pop()).
Also, depending on your particular case, you could rewrite the function to avoid using pop() or other functions that modify the argument. e.g.
def burninante(b):
return b[:-4:-1] # return the last three elements in reverse order

You can call burninate() with a copy of the list like this:
d = burninate(a[:])
or,
d = burninate(list(a))
The other alternative is to make a copy of the list in your method:
def burninate(b):
c=[]
b=b[:]
for i in range(3):
c.append(b.pop())
return c
>>> a = range(6)
>>> b = burninate(a)
>>> print a, b
>>> [0, 1, 2, 3, 4, 5] [5, 4, 3]

A slightly more readable way to do the same thing is:
d = burninate(list(a))
Here, the list() constructor creates a new list based on a.

A more general solution would be to import copy, and use copy.copy() on the parameter.

Other versions:
def burninate(b):
c = []
for i in range(1, 4):
c.append(b[-i])
return c
def burninate(b):
c = b[-4:-1]
c.reverse()
return c
And someday you will love list comprehensions:
def burninate(b):
return [b[-i] for i in range(1,4)]

You can use copy.deepcopy()

burninate = lambda x: x[:-4:-1]

Related

pass multiple arguments to a function

I wish to define a function that takes in 3 arguments,
each of which is a variable-length list as follows:
a = [1,2,3]
b = [4,5,6]
c = [7,8,9]
def functionName(*args1,*args2,*args3):
res1 = [i for i in args1]
res2 = [i for i in args2]
res3 = [i for i in args3]
return res1,res2,res3
Now i wish to call functionName as follows:
functionName(a,b,c)
and get the three lists back.
However, I get hit with the following error:
File "<ipython-input-178-8d50368fdacf>", line 15
def functionName(*args1,*args2,*args3):
^
SyntaxError: invalid syntax
How can I implement a function whose arguments contain variable-length lists/arrays?
Why not just declare the function as follows:
def functionName(list1, list2, list3):
res1 = [i for i in list1]
res2 = [i for i in list2]
res3 = [i for i in list3]
return res1, res2, res3
I think that should work as you want it to.
When you pass in *args into a function that allows you to pass in more than the formally defined number of positional arguments, i.e. if you had something like
def func(a,b,*args):
print(a,b)
for arg in args:
print(arg)
And then you called the function as
a = 10
b = 20
func(a, b, 35, 40)
your output would be
(10, 20)
35
40
You can also do something much simpler like this.
a = [1,2,3]
b = [4,5,6]
c = [7,8,9]
def functionName(*args1):
for i in range(len(args1)):
print (args1[i])
functionName(a,b,c)
This will result in:
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
Without the for loop, you can just give
print (args1)
It will give you the lists inside a tuple.
([1, 2, 3], [4, 5, 6], [7, 8, 9])
If you change it to:
functionName(a,c)
It will result in:
([1, 2, 3], [7, 8, 9])
Remember, *args1 will take in all the arguments you are sending and store them as a tuple.
See more details on *args and **kwargs here:
https://www.geeksforgeeks.org/args-kwargs-python/
https://realpython.com/python-kwargs-and-args/
As far as my understanding goes there you are passing 3 list objects to a function, so there is no need to type "*" before function parameter. List can be of variable size.
function(*args) is used when the number of parameters passed to the function are variable and unknown.
Ex:
def function(*args):
print(len(args))
>>> function(1,2,3,4)
4
>>> function(1,2)
2

Python2: access nested list using a list of indices

how do I access an element of a nested list with another list which contains the indices?
e.g:
# this is the variable containing the indices
a = [0, 1]
b = [[1,2],[3,4]]
in reality, these lists are filled with elements of self defined classes and the list containing the "coordinates" (a) has more than 2 elements.
Is there any possibility to access b[0][1] automatically? Previously, I used this code:
c = deepcopy(b)
for el in a:
c = c[el]
but since b is pretty big, I'd love to get rid of that deepcopy without manipulating b in reality.
I am happy about any suggestions :)
Thanks!
Just toss it in a function. That will keep it scoped so you don't overwrite the original value
def nested_getitem(container, idxs):
haystack = container
for idx in idxs:
haystack = haystack[idx]
return haystack
DEMO:
>>> a = [0, 1]
>>> b = [[1, 2], [3, 4]]
>>> nested_getitem(b, a)
2
You could probably do this with a functools.reduce as well, if you were insane.
import functools
import operator
def nested_getitem(container, idxs):
return functools.reduce(operator.getitem, idxs, container)

Python Variable assignment in a for loop

I understand that in Python regular c++ style variable assignment is replaced by references to stuff ie
a=[1,2,3]
b=a
a.append(4)
print(b) #gives [1,2,3,4]
print(a) #gives [1,2,3,4]
but I'm still confused why an analogous situation with basic types eg. integers works differently?
a=1
b=a
a+=1
print(b) # gives 1
print(a) # gives 2
But wait, it gets even more confusing when we consider loops!
li=[1,2,3]
for x in li:
x+=1
print(li) #gives [1,2,3]
Which is what I expected, but what happens if we do:
a,b,c=1,2,3
li=[a,b,c]
for x in li:
x+=1
print(li) #gives [1,2,3]
Maybe my question should be how to loop over a list of integers and change them without map() as i need a if statement in there. The only thing I can come up short of using
for x in range(len(li)):
Do stuff to li[x]
is packaging the integers in one element list. But there must be a better way.
Well, you need to think of mutable and immutable type.
For a list, it's mutable.
For a integer, it's immutable, which means you will refer to a new object if you change it. When a+=1 is executed, a will be assigned a new object, but b is still refer to the same one.
a=[1,2,3]
b=a
a.append(4)
print(b) #[1,2,3,4]
print(a) #[1,2,3,4]
Here you are modifying the list. The list content changes, but the list identity remains.
a=1
b=a
a+=1
This, however, is a reassignment. You assign a different object to a.
Note that if you did a += [4] in the 1st example, you would have seen the same result. This comes from the fact that a += something is the same as a = a.__iadd__(something), with a fallback to a = a.__add__(something) if __iadd__() doesn't exist.
The difference is that __iadd__() tries to do its job "inplace", by modifying the object it works on and returning it. So a refers to the same as before. This only works with mutable objects such as lists.
On immutable objects such as ints __add__() is called. It returns a different object, which leads to a pointing to another object than before. There is no other choice, as ints are immutable.
a,b,c=1,2,3
li=[a,b,c]
for x in li:
x+=1
print(li) #[1,2,3]
Here x += 1 means the same as x = x + 1. It changes where x refers to, but not the list contents.
Maybe my question should be how to loop over a list of integers and change them without >map() as i need a if statement in there.
for i, x in enumerate(li):
li[i] = x + 1
assigns to every list position the old value + 1.
The important thing here are the variable names. They really are just keys to a dictionary. They are resolved at runtime, depending on the current scope.
Let's have a look what names you access in your code. The locals function helps us: It shows the names in the local scope (and their value). Here's your code, with some debugging output:
a = [1, 2, 3] # a is bound
print(locals())
for x in a: # a is read, and for each iteration x is bound
x = x + 3 # x is read, the value increased and then bound to x again
print(locals())
print(locals())
print(x)
(Note I expanded x += 3 to x = x + 3 to increase visibility for the name accesses - read and write.)
First, you bind the list [1, 2, 3]to the name a. Then, you iterate over the list. During each iteration, the value is bound to the name x in the current scope. Your assignment then assigns another value to x.
Here's the output
{'a': [1, 2, 3]}
{'a': [1, 2, 3], 'x': 4}
{'a': [1, 2, 3], 'x': 5}
{'a': [1, 2, 3], 'x': 6}
{'a': [1, 2, 3], 'x': 6}
6
At no point you're accessing a, the list, and thus will never modify it.
To fix your problem, I'd use the enumerate function to get the index along with the value and then access the list using the name a to change it.
for idx, x in enumerate(a):
a[idx] = x + 3
print(a)
Output:
[4, 5, 6]
Note you might want to wrap those examples in a function, to avoid the cluttered global namespace.
For more about scopes, read the chapter in the Python tutorial. To further investigate that, use the globals function to see the names of the global namespace. (Not to be confused with the global keyword, note the missing 's'.)
Have fun!
For a C++-head it easiest tho think that every Python object is a pointer. When you write a = [1, 2, 3] you essentially write List * a = new List(1, 2, 3). When you write a = b, you essentially write List * b = a.
But when you take out actual items from the lists, these items happen to be numbers. Numbers are immutable; holding a pointer to an immutable object is about as good as holding this object by value.
So your for x in a: x += 1 is essentially
for (int x, it = a.iterator(); it->hasMore(); x=it.next()) {
x+=1; // the generated sum is silently discarded
}
which obviously has no effect.
If list elements were mutable objects you could mutate them exactly the way you wrote. See:
a = [[1], [2], [3]] # list of lists
for x in a: # x iterates over each sub-list
x.append(10)
print a # prints [[1, 10], [2, 10], [3, 10]]
But unless you have a compelling reason (e.g. a list of millions of objects under heavy memory load) you are better off making a copy of the list, applying a transformation and optionally a filter. This is easily done with a list comprehension:
a = [1, 2, 3, 0]
b = [n + 1 for n in a] # [2, 3, 4, 1]
c = [n * 10 for n in a if n < 3] # [10, 20, 0]
Either that, or you can write an explicit loop that creates another list:
source = [1, 2, 3]
target = []
for n in source:
n1 = <many lines of code involving n>
target.append(n1)
Your question has multiple parts, so it's going to be hard for one answer to cover all of them. glglgl has done a great job on most of it, but your final question is still unexplained:
Maybe my question should be how to loop over a list of integers and change them without map() as i need a if statement in there
"I need an if statement in there" doesn't mean you can't use map.
First, if you want the if to select which values you want to keep, map has a good friend named filter that does exactly that. For example, to keep only the odd numbers, but add one to each of them, you could do this:
>>> a = [1, 2, 3, 4, 5]
>>> b = []
>>> for x in a:
... if x%2:
... b.append(x+1)
Or just this:
>>> b = map(lambda x: x+1, filter(lambda x: x%2, a))
If, on the other hand, you want the if to control the expression itself—e.g., to add 1 to the odd numbers but leave the even ones alone, you can use an if expression the same way you'd use an if statement:
>>> for x in a:
... if x%2:
... b.append(x+1)
... else:
... b.append(x)
>>> b = map(lambda x: x+1 if x%2 else x, a)
Second, comprehensions are basically equivalent to map and filter, but with expressions instead of functions. If your expression would just be "call this function", then use map or filter. If your function would just be a lambda to "evaluate this expression", then use a comprehension. The above two examples get more readable this way:
>>> b = [x+1 for x in a if x%2]
>>> b = [x+1 if x%2 else x for x in a]
You can do something like this: li = [x+1 for x in li]

python list comprehension and extend() [duplicate]

This question already has answers here:
Why do these list operations (methods: clear / extend / reverse / append / sort / remove) return None, rather than the resulting list?
(6 answers)
Closed 5 months ago.
Working my way into Python (2.7.1)
But failing to make sense (for hours) of this:
>>> a = [1, 2]
>>> b = [3, 4]
>>>
>>> a.extend([b[0]])
>>> a
[1, 2, 3]
>>>
>>> a.extend([b[1]])
>>> a
[1, 2, 3, 4]
>>>
>>> m = [a.extend([b[i]]) for i in range(len(b))] # list of lists
>>> m
[None, None]
The first two extends work as expected, but when compacting the same in a list comprehension it fails.
What am i doing wrong?
extend modifies the list in-place.
>>> [a + b[0:i] for i in range(len(b)+1)]
[[1, 2], [1, 2, 3], [1, 2, 3, 4]]
list.extend() extends a list in place. Python standard library methods that alter objects in-place always return None (the default); your list comprehension executed a.extend() twice and thus the resulting list consists of two None return values.
Your a.extend() calls otherwise worked just fine; if you were to print a it would show:
[1, 2, 3, 4, 3, 4]
You don't see the None return value in the Python interpreter, because the interpreter never echoes None results. You could test for that explicitly:
>>> a = []
>>> a.extend(['foo', 'bar']) is None
True
>>> a
['foo', 'bar']
the return value of extend is None.
extend function extends the list with the value you've provided in-place and returns None. That's why you have two None values in your list. I propose you rewrite your comprehension like so:
a = [1, 2]
b = [3, 4]
m = [a + [v] for v in b] # m is [[1,2,3],[1,2,4]]
For python lists, methods that change the list work in place and return None. This applies to extendas well as to append, remove, insert, ...
In reply to an older question, I sketched an subclass of list that would behave as you expected list to work.
Why does [].append() not work in python?
This is intended as educational. For pros and cons.. look at the comments to my answer.
I like this for the ability of chaining methods and working in a fluent style, e.g. then something like
li = FluentList()
li.extend([1,4,6]).remove(4).append(7).insert(1,10).reverse().sort(key=lambda x:x%2)
would be possible.
a.extend() returns None.
You probably want one of these:
>>> m = a + b
>>> m
[1, 2, 3, 4]
>>> a.extend(b)
>>> a
[1, 2, 3, 4]
Aside from that, if you want to iterate over all elements of a list, you just can do it like that:
m = [somefunction(element) for element in somelist]
or
for element in somelist:
do_some_thing(element)
In most cases there is no need to go over the indices.
And if you want to add just one element to a list, you should use somelist.append(element) instead of `somelist.extend([element])

Python appending two returns to two different lists

I am wanting to append two returned lists to two different lists such as
def func():
return [1, 2, 3], [4, 5, 6]
list1.append(), list2.append() = func()
Any ideas?
You'll have to capture the return values first, then append:
res1, res2 = func()
list1.append(res1)
list2.append(res2)
You appear to be returning lists here, are you certain you don't mean to use list.extend() instead?
If you were extending list1 and list2, you could use slice assignments:
list1[len(list1):], list2[len(list2):] = func()
but this is a) surprising to newcomers and b) rather unreadable in my opinion. I'd still use the separate assignment, then extend calls:
res1, res2 = func()
list1.extend(res1)
list2.extend(res2)
Why not just storing the return values?
a, b = func() #Here we store it in a and b
list1.append(a) #append the first result to a
list2.append(b) #append the second one to b
With this, if a was previously [10] and b was previously [20], you'll have this result:
>>> a, b
[10, [1,2,3]], [20,[4,5,6]]
Nah, that wasn't difficult, was it?
By the way, you probably want to merge the lists. For this, you can use extend:
list1.extend(a)
Hope it helps!
A one line solution isn't possible (unless you use some cryptic hack, which is always a bad idea).
The best you can get is:
>>> list1 = []
>>> list2 = []
>>> def func():
... return [1, 2, 3], [4, 5, 6]
...
>>> a,b = func() # Get the return values
>>> list1.append(a) # Append the first
>>> list2.append(b) # Append the second
>>> list1
[[1, 2, 3]]
>>> list2
[[4, 5, 6]]
>>>
It's readable and efficient.

Categories