List item changed across multiple items in a list [duplicate] - python

This question already has answers here:
How to deep copy a list?
(10 answers)
Closed 5 years ago.
I understand where my problem is stemming from, I simply do not know how to fix it.
for x in listoflists:
if x[1] == [1,0]:
q=[0, 0]
for z in range(5):
q[z] = x[:]
q[z][1] = [0,1]
q[z][0][-1] = q[z][0][-1] * 1.25
print(id(q[z][0][-1]))
list.append(q)
What I am trying to do is:
go into some data that I have gathered and make copies where I have switched a data label from [1,0] to [0,1]
then go into the zero index and multiply the price column by some factor to scale it up.
What I currently have is changing all prior values to be what I want the last value to be.
I understand this is something to do with python handling lists as references.
I had to change my first line to be a slice of x to alleviate this, but I do not know how to get around the parts where I take specifically indexed list items because it is showing the id as two different memory spaces.
All of the solutions I have seen show list comprehensions but I do not know how that applies to my case, because it seems like my z indexing is working correctly and they all have separate memory ids.

You need to do deep copy. Slicing does shallow copy.
from copy import deepcopy
lst1 = ['a','b',['ab','ba']]
lst2 = deepcopy(lst1)
id(lst1[2])
# 4451912
id(lst2[2])
# 51003144

Related

Reversing a list/array in Python [duplicate]

This question already has answers here:
How do I reverse a list or loop over it backwards?
(37 answers)
Closed 2 years ago.
What are the differences between those four ways of reversing a list? Which one is faster?
1.
list.reverse()
2.
list(reversed(list))
3.
list[::-1]
4.
while(start < end):
list[start], list[end] = list[end], list[start]
start += 1
end -= 1`
I'll rename the list variable to xs to avoid confusion with the function list.
xs.reverse() mutates the original list, so that the values to the reference xs changes everywhere.
reversed(xs) creates an iterator, so calling list on it makes a new list with the values reversed. No mutations happen here.
xs[::-1] creates a new list reversed. This has the same effect as list(reversed(xs)). Not sure about which one is more performant though.
The procedure you wrote is also mutating, so that the original list xs changes.
If performance is not an issue, I would recommend non-mutating versions, as it can lead to surprising behaviour. In this case xs[::-1] is the simplest.
list.reverse()
Works on a reference. It means, that whenever you use it, you change the original list permanently.
list[::-1]
Is called reversing by slicing. It is not changing the state of the current list permanently. It is quite ineffective, since it creates a copy of the list that you are using it on, which takes up memory.
reversed()
Is not really that different. It works like slicing, but people often use it in for loop to create a reverse iterator, so a new list won't be created. If you print out a reversed list with reversed(), you can do it, but you will basically make a copy of a existing list, which is quite ineffective - but if you want to, you can do something like:
print(list(reversed(list_name)))
or if you want to print out elements of this reversed list
print(*reversed(list_name))
And I think the last solution works exactly like the first one, but it takes a lot of code. You have included only a part of it, but the full code would look something like that:
listt = [1,2,3,4,5,6,7]
start = 0
end = len(listt)-1
while(start < end):
listt[start], listt[end] = listt[end], listt[start]
start += 1
end -= 1
print(listt)
And that's a lot more code. Also, I am not pretty sure if it won't actually be slower.
It really depends on what you want in your program. If you simply want to show how the reversed list looks like, use slicing or reversed().
You can of course use them like the first method - reverse(), but you will have to assign those lists to a new variable, since (for example slicing) is not updating current list as mentioned above.
For example:
listt = [1,2,3,4,5,6,7]
a = listt[::-1]
print(listt)
#[1, 2, 3, 4, 5, 6, 7]
print(a)
#[7, 6, 5, 4, 3, 2, 1]

Why doesn't printing and extending in the same statement work? [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 3 years ago.
I made 2 lists (1 all strings, 1 all integers) using Python 3, tried to print and extend the first list with the other in the same statement, but ultimately got the output of none. Why?
# two lists, each with different data types
list_one = ["cat", "dog", "fish", "eye", "hook"]
list_two = [1, 2, 3, 4, 5]
# method that works and gives the expected output
list_one.extend(list_two)
print(list_one)
# method that doesn't work, gives an output of 'None'
print(list_one.extend(list_two))
Why does the second method give an output of 'None' instead of joining the two lists together?
Also, as I'm (very) new to programming, is it messy to put so many functions in a single statement?
print(list_one.extend(list_two))
This prints the return value of .extend(). extend has no return value/returns None. This is explicitly done so you're aware that extend modifies list_one in place, and doesn't return a new list. So you need to do this as a two-step process: modify the list, then print it.
You could do:
print(list_one + list_two)
This prints the "extended" list immediately, but doesn't modify either list and also doesn't store the extended list anywhere. To store the extended list you—again—need a two-step process:
list_three = list_one + list_two
print(list_three)
Get used to doing one thing at a time, instead of cramming everything into a single line. Readability is more important than line count. Python follows that doctrine and to some extend enforces it in ways like this.

Python and assignment [duplicate]

This question already has answers here:
List assignment to other list
(3 answers)
Closed 5 years ago.
I'm trying to understand (on the level of principle) the difference between assignment between (say) integers variables and list variables.
Integer case:
a=6
b=a
print(b) #prints 6
a=7
print(b) #prints 6
That makes sense to me with this logic: in the original b=a, b was given the same value as a (6), not identified with a. So if I change the value of a, the value of b does not change: b is not the same thing as a.
List case:
L=[1,2]
M = L
print(M)
L.append(6)
print(M)
This can make sense with this logic: in M=L I'm forcing M to literally be the same object as L is, I'm identifying it with L. So if L changes, so does M.
What doesn't make sense (to me) is why I need different logic in different cases. (Probably this has to do with integers being "immutable" and lists "mutable" but I don't see how that bears on this.) Can someone point me to a explanation? What is the principle behind the difference in behaviors? (I'm not looking so much for how the technical difference in implementation of integers and lists leads to the difference, but for the reason things were designed this way. What's the logic?)
Every name is a variable holding a reference to some object.
What happens in the first case is that
a = 6
b = a # (a and b point to the same object)
But here, you are changing what a points to:
a = 7
Compare this to the second/list situation, where you actually call the method on the first object. You didn't update the reference as you did in the case with the integers.
L = [1,2]
M = L # here you introduce a new name, referencing the same object as L.
L.append(6) # update that same object.
print(M) # you print that same object
You don't have different logic in different cases here. Lists and integers work in exactly the same way as far as assignment is concerned. If, in your second snippet, to assigned a different list to L in the penultimate line, the two variables would be unrelated.
But lists have an additional capability, which integers and strings don't have, which is that you can modify them. That's all you're​ seeing here.
Well M = L gets what L currently is and after it prints M you then append 6 to L but this will only effect L because M had only received what L previously was, but if you did M = L again after the append it would print the updated version of the list. Basically if you get a xvariable set to a yvariable and then that yvariable updates the xvariable will not update because you will have to update the xvariable again but this is usually this happens by it self if a loop is being used

Python2.7 - list.remove(item) within a loop gives unexpected behaviuor [duplicate]

This question already has answers here:
Removing Item From List - during iteration - what's wrong with this idiom? [duplicate]
(9 answers)
Closed 4 years ago.
I want to remove all even numbers in a list. But something confused me...
This is the code.
lst = [4,4,5,5]
for i in lst:
if i % 2 == 0:
print i
lst.remove(i)
print lst
It prints [4, 5, 5] Why not [5, 5]?
It should be like this
for i in lst[:]:
if i % 2 == 0:
print i
lst.remove(i)
print lst
Problem:
You are modifying the list while you iterate over it. Due to which the iteration is stopped before it could complete
Solution:
You could iterate over copy of the list
You could use list comprehension :
lst=[i for i in lst if i%2 != 0]
By using list.remove, you are modifying the list during the iteration. This breaks the iteration giving you unexpected results.
One solution is to create a new list using either filter or a list comprehension:
>>> filter(lambda i: i % 2 != 0, lst)
[5, 5]
>>> [i for i in lst if i % 2 != 0]
[5, 5]
You can assign either expression to lst if needed, but you can't avoid creating a new list object with these methods.
Other answers have already mentioned that you're modifying the list while iterating over it, and offered better ways to do it. Personally I prefer the list comprehension method:
odd_numbers = [item for item in numbers if item % 2 != 0]
For your specified case of a very small list, I would definitely go with that.
However, this does create a new list, which could be a problem if you have a very large list. In the case of integers, large probably means millions at least, but to be precise, it's however large it needs to be to start giving you issues with memory usage. In that case, here are a couple ways to do it.
One way is similar to the intent of the code in your question. You iterate over the list, removing the even numbers as you go. However, to avoid the problems that modifying a list you're iterating over can cause, you iterate over it backwards. There are ways to iterate forward, but this is simpler.
Here's one way using a while loop:
# A one hundred million item list that we don't want to copy
# even just the odd numbers from to put into a new list.
numbers = range(100000000) # list(range(100000000)) in Python 3
index = len(numbers) - 1 # Start on the index of the last item
while index >= 0:
if numbers[index] % 2 == 0:
numbers.pop(index)
index -= 1
Here's another way using a for loop:
# A one hundred million item list that we don't want to copy
# even just the odd numbers from to put into a new list.
numbers = range(100000000) # list(range(100000000)) in Python 3
for index in xrange(len(numbers) - 1, -1, -1): # range(...) in Python 3
if numbers[index] % 2 == 0:
numbers.pop(index)
Notice in both the while loop and for loop versions, I used numbers.pop(index), not numbers.remove(numbers[index]). First of all, .pop() is much more efficient because it provides the index, whereas .remove() would have to search the list for the first occurrence of the value. Second, notice that I said, "first occurrence of the value". That means that unless every item is unique, using .remove() would remove a different item than the one the loop is currently on, which would end up leaving the current item in the list.
There's one more solution I want to mention, for situations where you need to keep the original list, but don't want to use too much more memory to store a copy of the odd numbers. If you only want to iterate over the odd numbers once (or you're so averse to using memory that you'd rather recalculate things when you need to), you can use a generator. Doing so would let you iterate over the odd numbers in the list without needing any additional memory, apart from the inconsequential amount used by the generator mechanism.
A generator expression is defined exactly like a list comprehension, except that it's enclosed in parentheses instead of square brackets:
odd_numbers = (item for item in numbers if item % 2 != 0)
Remember that the generator expression is iterating over the original list, so changing the original list mid-iteration will give you the same problems as modifying a list while iterating over it in a for loop. In fact, the generator expression is itself using a for loop.
As an aside, generator expressions shouldn't be relegated only to very large lists; I use them whenever I don't need to calculate a whole list in one go.
Summary / TLDR:
The "best" way depends exactly what you're doing, but this should cover a lot of situations.
Assuming lists are either "small" or "large":
If your list is small, use the list comprehension (or even the generator expression if you can). If it's large, read on.
If you don't need the original list, use the while loop or for loop methods to remove the even numbers entirely (though using .pop(), not .remove()). If you do need the original list, read on.
If you're only iterating over the odd numbers once, use the generator expression. If you're iterating over them more than once, but you're willing to repeat computation to save memory, use the generator expression.
If you're iterating over the odd numbers too many times to recompute them each time, or you need random access, then use a list comprehension to make a new list with only the odd numbers in them. It's going to use a lot of memory, but them's the breaks.
As a general principle, you should not modify a collection while you are iterating over it. This leads to skipping of some elements, and index error in some cases.
Instead of removing elements from list, it would be easier if you just create another reference with same name. It has lesser time complexity too.
lst = filter(lambda i: i % 2 !=0, lst)

Python: Array v. List [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Python List vs. Array - when to use?
I'm working on a few projects in Python, and I have a few questions:
What's the difference between Arrays and Lists?
If it's not obvious from question 1, which should I use?
How do you use the preferred one? (create array/list, add item, remove item, pick random item)
Use lists unless you want some very specific features that are in the C array libraries.
python really has three primitive data structures
tuple = ('a','b','c')
list = ['a','b','c']
dict = {'a':1, 'b': true, 'c': "name"}
list.append('d') #will add 'd' to the list
list[0] #will get the first item 'a'
list.insert(i, x) # Insert an item at a given position. The first argument is the index of the element before which to insert, so a.insert(0, x) inserts at the front of the list, and a.insert(len(a), x) is equivalent to a.append(x).
list.pop(2) # will remove items by position (index), remove the 3rd item
list.remove(x) # Remove the first item from the list whose value is x.
list.index(x) # Return the index in the list of the first item whose value is x. It is an error if there is no such item.
list.count(x) # Return the number of times x appears in the list.
list.sort(cmp=None, key=None, reverse=False) # Sort the items of the list in place (the arguments can be used for sort customization, see sorted() for their explanation).
list.reverse() # Reverse the elements of the list, in place.
More on data structures here:
http://docs.python.org/tutorial/datastructures.html
Nothing really concrete here and this answer is a bit subjective...
In general, I feel you should use a list just because it is supported in the syntax and is used more widely in the other libraries, etc.
You should use arrays if you know that everything in the "list" will be of the same type and you want to store the data more compactly.

Categories

Resources