I was doing some exercises in python python and I bumped into a problem. I already printed a list reversed and I wanted to print the reversed elements in separate lines too but for some reason it only prints the first one and if I put the first print into comment it prints out the loop just fine. Why?
def main():
pass
if __name__ == '__main__':
main()
list1 = [10, 20, 30, 40, 50]
def reverse_list(l1):
l2 = []
for i in range(len(l1)):
l2.append(l1.pop())
return l2
print(reverse_list(list1))
pass
def reverse_print(l):
listr = reversed(l)
for j in listr:
print(j)
reverse_print(list1)
print("Complete...")
In python, data types are divided into mutable and immutable types:
Mutable object: the value in the memory pointed to by the object can be changed
Immutable object: The value in the memory pointed to by the object cannot be changed, so when the value pointed to by the variable changes, it is equivalent to copying the original value and storing it in a new address, and the variable points to this new address.
a = [1,2,3,4]
a_ref = a
print(a) # [1,2,3,4]
a_ref[0] = 5
print(a) # [5,2,3,4]
It can be seen from the above code
a, a_ref are exactly the same
a and a_ref have the same memory location
modification to a_ref, a will also be changed
for example:
a = [1,2,3,4]
def somefunc(a2):
a2.append(5)
print(a) # [1,2,3,4]
somefunc(a)
print(a) # [1,2,3,4,5]
Normally, it won't change a by calling somefunc(a)
BUT
Since a2 is a reference to a and points to the same memory location, when a2 is modified, it will also be modified to a
in your code:
when you calling reverse_list(list1), l1 is a reference to list1, when you pop the element from l1, it will also pop the element in list1
so after called reverse_list(list1), list1 is empty, that why reverse_print(list1) do not print anything
Sollution
def reverse_list(l1):
l1_copy = l1[:] # copy the whole list
l2 = []
for i in range(len(l1_copy)):
l2.append(l1_copy.pop())
return l2
when you call l1_copy = l1[:]
than l1_copy won't point to same memory location
so l1 and list1 won't modified when modifica l1_copy
PS: english is my second langage, so there maybe have some gramma mistake, please excuse me
at the second print list1 has been emptied out from using the pop() method in reverse_list.
to reverse a list perhaps you should use the reverse()method like the following example:
List1 = [1,2,3]
List1.reverse()
print(List1) # [3,2,1]
Related
I stumbled upon a theoretical question about how python works, and it got me puzzled. I tried to understand exactly what happened but couldn't find the answer in google - I'm a beginner, so I don't even know the terminology to make the apropriate search.
On the following code, when calling the function it changes myList, while I only wanted to create a list2 which was a copy of list1 (myList).
myList = [1,2,3,4,5,(1,2),(3,4)]
def onlyTuples(list1):
list2 = list1 # here is my question
for index,e in enumerate(list2):
if type(list2[index]) is not tuple:
list2[index] = (list2[index],)
return(list2)
print(myList)
create_new_list = onlyTuples(myList) # triggered by this call
print(myList)
It's all good if I change list2 = list1 to list2 = list(list1) and myList won't be changed when calling the function, but why?
The same thing doesn't happen with something like this:
a = 6
b = a
b = 7
print(a)
Any light upon the question will be appreciated. Thanks!
In python lists are passed by reference, so when you pass list to a function you pass its address in the memory. list2 = list1 won't create a copy of the list, it will save in list2 the address saved in list1. so change of list2 will change list1, but the function in the class list doesn't save the address, it copy a sequence to a list
To make a copy of a list, use:
newList = myList.copy()
I have written the python code in the following form
temp=[]
x=[1,2,3]
for i in range(4):
temp=temp+[x]
temp[1][1]=x[1]+1
print temp[0]
print temp[1]
Here, I wanted the value of just temp[1][1], but the value of temp[0][1] also gets changed. Is there a way of changing just one value? I created a new list and tried to add it to temp, but that does not seem to work as well.
Update:
Thanks, but it did not seem to work in my case (which was a multi dimensional array). I have the code has follows:
tempList=[]
for i in range(openList[0].hx):
tempList=tempList+[copy.copy(abc)]
tempList[0][0][0]=123
print sudokuList
Here abc is a two dimensional list. Modifying the value of tempList[0][0][0] changes the value of tempList[1][0][0] and so on.
That's because of that you are assigning the x to all of your list items so all of them are references to one object and once you change one on them actually you have changed all of them. for getting ride of this problem you can use a list comprehension to define the temp list :
temp=[[1,2,3] for _ in range(4)]
temp[1][1]=7
print temp[0]
print temp[1]
result :
[1, 2, 3]
[1, 7, 3]
This is actually a common error for beginners to Python: How to clone or copy a list?
When you add x to temp four times, you're creating a temp which has the same x four number of times.
So, temp[0], temp[2], temp[3] and temp[4] are all pointing to the same x you declared at the first line.
Just make a copy when adding:
temp=[]
x=[1,2,3]
for i in range(4):
temp=temp.append(x[:])
temp[1][1]=x[1]+1
print temp[0]
print temp[1]
You can see it with id function, which returns a different value for different objects:
>>> temp=[]
>>> x=[1,2,3]
>>> for i in range(4):
... temp=temp+[x]
...
>>> id(temp[0]), id(temp[1])
(4301992880, 4301992880) # they're the same
>>> temp=[]
>>> x=[1,2,3]
>>> for i in range(4):
... temp=temp+[x[:]]
...
>>> id(temp[0]), id(temp[1])
(4301992088, 4302183024) # now they are not
Try, the following. x in for loop is a reference to the original x and not a copy. Because of this reference, changing any element reflects on all objects. So you would need to make a copy as used in following snippet.
temp=[]
x=[1,2,3]
for i in range(4):
temp=temp+[x[:]]
temp[1][1]=x[1]+1
print temp[0]
print temp[1]
----EDIT----
As per your comment, use copy.deepcopy to copy the list. deepcopy would recursively copy all the referenced elements inside the list. Check copy.deepcopy. So the code looks like:-
import copy
temp=[]
x=[1,2,3]
for i in range(4):
x_copy = copy.deepcopy(x)
#do something with x_copy. use this inplace of x in your code.
#will work for 1D or 2D or any other higher order lists.
See my code in python 3.4. I can get around it fine. It bugs me a little. I'm guessing it's something to do with foo2 resetting a rather than treating it as list 1.
def foo1(a):
a.append(3) ### add element 3 to end of list
return()
def foo2(a):
a=a+[3] #### add element 3 to end of list
return()
list1=[1,2]
foo1(list1)
print(list1) ### shows [1,2,3]
list1=[1,2]
foo2(list1)
print(list1) #### shows [1,2]
In foo2 you do not mutate the original list referred to by a - instead, you create a new list from list1 and [3], and bind the result which is a new list to the local name a. So list1 is not changed at all.
There is a difference between append and +=
>>> a = []
>>> id(a)
11814312
>>> a.append("hello")
>>> id(a)
11814312
>>> b = []
>>> id(b)
11828720
>>> c = b + ["hello"]
>>> id(c)
11833752
>>> b += ["hello"]
>>> id(b)
11828720
As you can see, append and += have the same result; they add the item to the list, without producing a new list. Using + adds the two lists and produces a new list.
In the first example, you're using a method that modifies a in-place. In the second example, you're making a new a that replaces the old a but without modifying the old a - that's usually what happens when you use the = to assign a new value. One exception is when you use slicing notation on the left-hand side: a[:] = a + [3] would work as your first example did.
I create many object then I store in a list. But I want to delete them after some time because I create news one and don't want my memory goes high (in my case, it jumps to 20 gigs of ram if I don't delete it).
Here is a little code to illustrate what I trying to do:
class test:
def __init__(self):
self.a = "Hello World"
def kill(self):
del self
a = test()
b = test()
c = [a,b]
print("1)Before:",a,b)
for i in c:
del i
for i in c:
i.kill()
print("2)After:",a,b)
A and B are my objects. C is a list of these two objects. I'm trying to delete it definitely with a for-loop in C: one time with DEL and other time with a function. It's not seem to work because the print continue to show the objects.
I need this because I create 100 000 objects many times. The first time I create 100k object, the second time another 100k but I don't need to keep the previous 100k. If I don't delete them, the memory usage goes really high, very quickly.
tl;dr;
mylist.clear() # Added in Python 3.3
del mylist[:]
are probably the best ways to do this. The rest of this answer tries to explain why some of your other efforts didn't work.
cpython at least works on reference counting to determine when objects will be deleted. Here you have multiple references to the same objects. a refers to the same object that c[0] references. When you loop over c (for i in c:), at some point i also refers to that same object. the del keyword removes a single reference, so:
for i in c:
del i
creates a reference to an object in c and then deletes that reference -- but the object still has other references (one stored in c for example) so it will persist.
In the same way:
def kill(self):
del self
only deletes a reference to the object in that method. One way to remove all the references from a list is to use slice assignment:
mylist = list(range(10000))
mylist[:] = []
print(mylist)
Apparently you can also delete the slice to remove objects in place:
del mylist[:] #This will implicitly call the `__delslice__` or `__delitem__` method.
This will remove all the references from mylist and also remove the references from anything that refers to mylist. Compared that to simply deleting the list -- e.g.
mylist = list(range(10000))
b = mylist
del mylist
#here we didn't get all the references to the objects we created ...
print(b) #[0, 1, 2, 3, 4, ...]
Finally, more recent python revisions have added a clear method which does the same thing that del mylist[:] does.
mylist = [1, 2, 3]
mylist.clear()
print(mylist)
Here's how you delete every item from a list.
del c[:]
Here's how you delete the first two items from a list.
del c[:2]
Here's how you delete a single item from a list (a in your case), assuming c is a list.
del c[0]
If the goal is to delete the objects a and b themselves (which appears to be the case), forming the list [a, b] is not helpful. Instead, one should keep a list of strings used as the names of those objects. These allow one to delete the objects in a loop, by accessing the globals() dictionary.
c = ['a', 'b']
# create and work with a and b
for i in c:
del globals()[i]
To delete all objects in a list, you can directly write list = []
Here is example:
>>> a = [1, 2, 3]
>>> a
[1, 2, 3]
>>> a = []
>>> a
[]
Below is non-working and working code which generates a list of lists.
Example 1 does not work correctly, it repeats the last list appended over and over.
Example 2, where I replaced delete with creating a new list does work correctly.
# Example 1, this does not work correctly
l1 = []
l2 = []
x = 0
for n in range(0,3):
del l1[:] # deleting all list elements
for i in range(0,3):
l1.append(x)
x+=1
l2.append(l1)
print(l2)
# Example 2, this works correctly
l2 = []
x = 0
for n in range(0,3):
l1 = [] # creating the list each loop through
for i in range(0,3):
l1.append(x)
x+=1
l2.append(l1)
print(l2)
In your first example, l1 is the same list object the entire time. When you do l2.append(l1), you insert a reference to this l1 object into l2. When the loop restarts and you delete everything in l1, you also delete everything in the list inside l2, because that is the same list. You are appending the same list object to l2 multiple times, so every time you clear it, you clear every list in l2.
In the second example, you create a separate list object every time. Each list object is thus independent, and clearing one doesn't affect the others.
Visualization of #1 code - http://goo.gl/6ICqo
l2 and l1 refer to the same object (list). When you delete all the elements of the list, the change is reflected in l2 and l1.
Visualization of #2 code - http://goo.gl/fkoqh
Here, when the code executes l1 = [], l1's reference gets re-assigned. But l2's elements keep referring to the original object.