Creating a back-up list in python - python

I want to create a back-up list of another list in python. Here is an example of the code.
x = [1,2,3]
y = x
x.pop(0)
print y
This however yields the result y = [2,3] when I want it to yield [1,2,3]. How would I go about making the y list independent of the x list?

A common idiom for this is y = x[:]. This makes a shallow copy of x and stores it in y.
Note that if x contains references to objects, y will also contain references to the same objects. This may or may not be what you want. If it isn't, take a look at copy.deepcopy().

Here is one way to do it:
import copy
x = [1,2,3]
y = copy.deepcopy(x)
x.pop(0)
print x
print y
from the docs here

While aix has the most parsimonious answer here, for completeness you can also do this:
y = list(x)
This will force the creation of a new list, and makes it pretty clear what you're trying to do. I would probably do it that way myself. But be aware- it doesn't make a deep copy (so all the elements are the same references).
If you want to make sure NOTHING happens to y, you can make it a tuple- which will prevent deletion and addition of elements. If you want to do that:
y = tuple(x)
As a final alternative you can do this:
y = [a for a in x]
That's the list comprehension approach to copying (and great for doing basic transforms or filtering). So really, you have a lot of options.

Related

Need help understanding a specific type of for loop [duplicate]

This question already has answers here:
What does "list comprehension" and similar mean? How does it work and how can I use it?
(5 answers)
Closed 6 months ago.
I am still relatively new to coding. I've been doing this for less than a year and most of the basics I completely understand now. However, every now and then I come across a type of for loop that I can't get my head around.
It usually goes like this:
x for x in list if x in otherList
I completley understand for loops and if statements. But that particular line of code always confuses me. Would anyone be able to provide a detailed explanation of what actually is happening there, please?
It's called a list comprehension if it's in brackets []:
This:
new_list = [x for x in my_list if x in other_list]
Is equivalent to this:
new_list = []
for x in my_list:
if x in other_list:
new_list.append(x)
If it's in parentheses () it's called a generator:
This:
new_list = (x for x in my_list if x in other_list)
Is sort of equivalent to this:
def foo():
for x in my_list:
if x in other_list:
yield x
new_list = foo()
You might want to read this question and answer to understand more about generators and yielding functions.
This is used within a list comprehension and the if statement acts as a filter.
You may begin with a list of all numbers from 0-9:
mynums = range(10)
But then you might want only the even numbers in a new list:
myevennums=[]
for i in mynums:
if mynums%2 ==0:
myevennums.append(i)
That works but so many keystrokes 😭
So python allows list comprehensions:
myevennums = [i for i in mynums if i%2==0]
The condition could be anything, including membership in another list:
evens_to_20 = list(range(0,21,2))
Then you could create a list with the elements of your list that are also in the other list:
myevennums = [i for i in mynums if i in evens_to_20]
In general, when you see a statement like that you can always expand it as:
Y_comprehension = [i for i in X if condition(i)]
# the same as
Y_loop=[]
for i in X:
if condition(i):
Y_loop.append(i)
assert Y_comprehension == Y_loop
If the condition really is very simple, the list-comprehension is usually the better choice. If it starts to get complicated, you probably want the loop or an intermediate function def condition(item): ...

Issue understanding modification of variable in jupyter notebook

I'm quite new to jupyter and just can't figure out how this work :
With these 3 blocks of code, a multiple execution of the 3rd one make n incremental, i.e. [1,2], then [1,3], [1,4], etc
n = [1,2]
--
def fonction(x):
y=x
y[1]=x[1]+1
return y
--
res = fonction(n)
print(res)
This is the minimal example i can provide. I don't understand why the variable n is modified though it's only the argument in the 3rd block (and the 2nd block isn't modifying the argument of the function !)
Thanks a lot for your patience explaining 101 jupyter to this old engineer, sincerely
Since you are writing y = x inside of fonction that is not giving y the value contained in x, but rather making y point to the value of x. In this case y is pointing to x and x is pointing to n (via input of fonction) therefore y[1] = will assign a value to the variable which is stored in the location that y is pointing to... Since y is pointing to the same location as n it changes that list. This means that when you reference n again it is still pointing to the same list, but the list has been modified via y[1] = x[1] + 1.
You can fix this issue by using y = x[:] which will create an entirely new list for y so that it does not point to the list stored in n.
This can be applied to your code like so:
def fonction(x):
y=x[:] #notice the change here
y[1]=x[1]+1
return y

Python for element in list matching condition

I have found myself frequently iterating over a subset of a list according to some condition that is only needed for that loop, and would like to know if there is a more efficient way to write this.
Take for example the list:
foo = [1, 2, 3, 4, 5]
If I wanted to build a for loop that iterates through every element greater than 2, I would typically do something like this:
for x in [y for y in foo if y > 2]:
# Do something
However this seems redundant, and isn't extremely readable in my opinion. I don't think it is particularly inefficient, especially when using a generator instead as #iota pointed out below, however I would much rather be able to write something like:
for x in foo if x > 2:
# Do something
Ideally avoiding the need for a second for and a whole other temporary variable. Is there a syntax for this? I use Python3 but I assume any such syntax would likely have a Python2 variant as well.
Note: This is obviously a very simple example that could be better handled by something like range() or sorting & slicing, so assume foo is any arbitrary list that must be filtered by any arbitrary condition
Not quite the syntax you are after but you can also use filter using a lambda:
for x in filter(lambda y: y > 2, foo):
print(x)
Or using a function for readbility sake:
def greaterthantwo(y):
return y > 2
for x in filter(greaterthantwo, foo):
print(x)
filter also has the advantage of creating a generator expression so it doesn't evaluate all the values if you exit the loop early (as opposed to using a new list)
There's filter as discussed in #salparadise but you can also use generators:
def filterbyvalue(seq, value):
for el in seq:
if el.attribute==value:
yield el
for x in filterbyvalue(foo,2):
#Do something
It may look bigger but it is useful when you have to do something complex instead of filtering, it is also performes better than first creating a list comprehension and then looping over it.
I would do like this
For efficient code
data = (x for x in foo if x>2)
print(next(data))
For more readable code
[print(x) for x in foo if x>2]

Python list comprehension with function returning a list

I am trying to call a function for a range of values. That function returns a list. The goal is to combine all the returned lists into a list.
Here is a test function that returns a list:
def f(i):
return [chr(ord('a') + i), chr(ord('b') + i), chr(ord('c') + i)]
Here is a list comprehension that does what I need that I came up with after some experimentation and a lot of StackOverflow reading:
y = [a for x in (f(i) for i in range(5)) for a in x]
However, I do not understand why and how it works when a simple loop that solves this problem looks like this:
y = []
for x in (f(i) for i in range(5)):
for a in x:
y.append(a)
Can someone explain?
Thanks!
This may be a better illustration, following Bendik Knapstad's answer:
[
a # element added to the list
for x in (f(i) for i in range(5)) # outer loop
for a in x # inner loop that assigns to element to be added to the list
]
Answering to this:
However, I do not understand why and how it works (list comprehensions) when a simple loop that solves this problem looks like this (for loops)
Yes, they both can work but there are some differences.
First, with list comprehensions, you are able to generate a list (because that's the output) after assigning it to a variable. Whereas in a for loop you must have the list created (regardless if it's empty or not) if you wish to use append later on perform any updating/deleting/re-indexing operation.
Second, simplicity. While for loops might be used in complex tasks where you need to apply a wide variety of functions, and maybe use RNGs, list comprehensions are always preferrable when it comes to dealing with lists and performing rather 'basic' operations (of course you can start nesting them and turn them into something more complex).
Third and finally, speed. List comprehensions tend to perform baster when compared to for loops for simple tasks.
More in-depth information regarding listcomp and for loops can be read in python's official tutorial. https://docs.python.org/3/tutorial/datastructures.html
Nested list comprehensions are hard to read.
But if you look at the two expressions you'll se that they contain the same logic.
In the list comprehension the first a is the part you want to keep in the list. It's equal to the y.append(a) in the for loop.
The for x in (f(i) for i in range(5)) is the same as in your for loop
The same goes for the next line for a in x
So for x in (f(i) for i in range(5)) creates a list x
So if we had the list x already we could write
y= [a for a in x]

How to change the value of a reference contained inside a list?

In python how can i change the value of references contained inside a list ?
For example in the following code
x = 'stack'
y = 'exchange'
l = [x,y]
l[1] = 'overflow'
The last line would actually replace y with 'overflow' but what I want to do is change the reference contained at l[1] to 'overflow' how can I achieve the same ?
PS : y = 'overflow' is not an accepted answer :P
The context probably will make the problem clearer there are 50 variable that I have inside a list initialised to zero and I want them to set their values at run-time.so that when at a later point of time I do a print variable50 , i get the new value not zero.
You do rebind reference at l[1] to 'overflow' with l[1] = 'overflow'. If you want to actually modify the string object, you can't. Strings are immutable.
"change the value of references" is awkward phrasing and there are two separate things that I can imagine that you mean:
1) cause the element of the list to refer to something else; but that's what your example does and is what you say you don't want.
2) cause the referred-to element of the list to change. The referred-to element is an object, so this is only possible by invoking code that changes the object's state. Python's str type is immutable, so no such code exists for the example situation.
What are you really trying to do? Idiomatic Python code embraces reference semantics rather than trying to fight them.
You want to add a new element to your list and sort it on position 1? That can be achieved with basic list functions.. Just a question why aren't you using dictionaries?
Is this interesting ?
x = 'stack'
y = 'exchange'
z = 'overflow'
l = [x, y]
l
['stack', 'exchange']
use python's swap
y, z = z, y
but then, you have to re-assign the list
l = [x, y]
l
['stack', 'overflow']

Categories

Resources