Python list append issue - python

One of my function appends an item to the list. This appended list is then sent as argument into two recursive function. But when one of the called function updates its own local list, the original list gets modified because of which second recursive function gets modified list as input.
Basically this is a sort of thing that i am trying to do. Here i want
[2]
[2,3]
[2,4] as output but i am getting [2][2,3] as only output as the original list is getting modified. Is there any way that i can send same list in two functions.
def Growtree(llst, x):
if len(llst) == 2:
return
llst.append(x)
print(llst)
Growtree(llst,3)
Growtree(llst,4)

When Growtree(llst, 4) is called there is already 2 and 3 in the list llst. So it returns without appending a new element because of your if.
What you need is to make a copy of the list (before to call Glowtree of inside, it depends if you want the orignal list to get modified).
To copy a list, see https://stackoverflow.com/a/2612815/3410584

use copy.deepcopy()
The reason why the original list got modified in your functions, is that generally when you passing an mutable obj in python. It's the reference got passed into.

Related

Why does Python return None on list.reverse()?

Was solving an algorithms problem and had to reverse a list.
When done, this is what my code looked like:
def construct_path_using_dict(previous_nodes, end_node):
constructed_path = []
current_node = end_node
while current_node:
constructed_path.append(current_node)
current_node = previous_nodes[current_node]
constructed_path = reverse(constructed_path)
return constructed_path
But, along the way, I tried return constructed_path.reverse() and I realized it wasn't returning a list...
Why was it made this way?
Shouldn't it make sense that I should be able to return a reversed list directly, without first doing list.reverse() or list = reverse(list) ?
What I'm about to write was already said here, but I'll write it anyway because I think it will perhaps add some clarity.
You're asking why the reverse method doesn't return a (reference to the) result, and instead modifies the list in-place. In the official python tutorial, it says this on the matter:
You might have noticed that methods like insert, remove or sort that only modify the list have no return value printed – they return the default None. This is a design principle for all mutable data structures in Python.
In other words (or at least, this is the way I think about it) - python tries to mutate in-place where-ever possible (that is, when dealing with an immutable data structure), and when it mutates in-place, it doesn't also return a reference to the list - because then it would appear that it is returning a new list, when it is really returning the old list.
To be clear, this is only true for object methods, not functions that take a list, for example, because the function has no way of knowing whether or not it can mutate the iterable that was passed in. Are you passing a list or a tuple? The function has no way of knowing, unlike an object method.
list.reverse reverses in place, modifying the list it was called on. Generally, Python methods that operate in place don’t return what they operated on to avoid confusion over whether the returned value is a copy.
You can reverse and return the original list:
constructed_path.reverse()
return constructed_path
Or return a reverse iterator over the original list, which isn’t a list but doesn’t involve creating a second list just as big as the first:
return reversed(constructed_path)
Or return a new list containing the reversed elements of the original list:
return constructed_path[::-1]
# equivalent: return list(reversed(constructed_path))
If you’re not concerned about performance, just pick the option you find most readable.
methods like insert, remove or sort that only modify the list have no return value printed – they return the default None. 1 This is a design principle for all mutable data structures in Python.
PyDocs 5.1
As I understand it, you can see the distinction quickly by comparing the differences returned by modifying a list (mutable) ie using list.reverse() and mutating a list that's an element within a tuple (non-mutable), while calling
id(list)
id(tuple_with_list)
before and after the mutations. Mutable data-type mutations returning none is part allowing them to be changed/expanded/pointed-to-by-multiple references without reallocating memory.

Copy 2d array in Python: Confusion

My understanding is that:
def copy_2d(p):
return list(p)
Would make a full copy of p and return it as a result. list( p) seems to do this when I try it in the repl. However it seems like calling the above method like this:
b = [[1,2,3],[3,4,5]]
a = copy_2d(b)
a[0][0] = 0
if (b[0][0] == 0): print "Huh??"
It prints "Huh??", that is, it appears that b is just a reference to a. I double checked but I might be blind. Can someone clarify please?
Your current code for copy_2d returns a shallow copy of the list you pass as its argument. That is, you're creating a new outer list, but the inner values (which may be lists themselves) are not copied. The new list references the same inner lists, so when you mutate one of them, you'll see the same change in the shallow copy as you do in the original list.
You can fix the issue by copying the inner lists as well as creating a new outer list. Try:
def copy_2d(p):
return map(list, p) # warning: this only works as intended in Python 2
In Python 2, this works because map returns a list. In Python 3, the map function returns an iterator, so a list comprehension like [list(inner) for inner in p] would be better.
Of course, if you don't need to write your own code to solve this problem, you should just use copy.deepcopy from the standard library.
import copy
def copy_2d(p):
return copy.deepcopy(p)
or
def copy_2d(p):
return [list(p2) for p2 in p]
What you did copied the array with all the values inside. But inside you had objects with were arrays. Your function did not copied inside lists but their references.
The second solution still copies references, but one layer below.
Shallow copy made. Same logic as here?what-does-the-list-function-do-in-python
"list() converts the iterable passed to it to a list. If the iterable is already a list then a shallow copy is returned, i.e only the outermost container is new rest of the objects are still the same."

Reflection of a modified list outside the function [duplicate]

This question already has answers here:
Why can a function modify some arguments as perceived by the caller, but not others?
(13 answers)
Closed 8 years ago.
I was currently doing the introductory course of python provided by mit on edx. And, the professor is currently teaching the use of functions as objects. I am having confusion in one of the codes where he is defining a function and taking two arguments- one is a list and other is some other function.
def applyToEach(l,f):
for i in range(len(l)):
l[i]=f(l[i])
l=[1,-2,3.4]
applyToEach(l,abs)
print(l)
My question is: the changes/modifications in the list is taking place inside the function. So, how they are getting reflected outside the function? According to me, there should not be any changes in the list when I print it because I made all the changes inside the function. I mean that different environments are created for different functions and it worked this way only in the previous exercises and therefore changes are not reflected in our main environment. Correct me wherever I am wrong, because when I am running this code, the modified list is getting printed. So, where I am wrong in the given logic?
Because they are the same objects. You can verify that through id() or is.
#!/usr/bin/env python
#-*- coding:utf-8 -*-
def applyToEach(l,f):
print 'inside id is: ', id(l)
for i in range(len(l)):
l[i]=f(l[i])
l=[1,-2,3.4]
print 'outside id is: ', id(l)
applyToEach(l,abs)
print(l)
In Python, passing a value to a function equals 'para = value'. However, '=' only add a reference to the value but not a copy.
The list is being passed as an argument to the function. When a list is created in python a memory location is allocated to it for reference. Now, when you are passing the list (or even do any operation on the list), the assigned memory location is passed. So, whatever modifications are done, that is reflected in the list itself. But the memory location remains the same. So, regardless of whether you access it from outside of the function or inside the function it points to the same memory location. And hence, the changes done, inside are visible outside the function.
Let the list be
Fruit = ['Mango', 'Apple', 'Orange']
While we assign the list to value Fruit, a memory location is assigned to it (here 4886)
so id(Fruit) = 4886
Now, suppose the list 'Fruit' is being passed to a function, inside which it is being modified as follows :-
def fun(li=[]):
li[1] = 'Grape'
li.extend(['Apple', 'Banana'])
So, what happens behind the scene is, while the list is passed to the method as argument, actually, the reference (or address) of the list is passed. So, even if you access the list and modify it within the function, it modifies the original list as it refers to the list reference. So, the list reference get updated accordingly.
After, the modification also, it will show the list updated as inside the method. But, the memory location will be the same i.e. id(Fruit) = 4886
Even if you assign the list to a new list i.e. temp = Fruit, same will be the scenario. temp will refer to the same location as of Fruit, both the list will point to the same location.
To avoid this situation, what you have to do is :-
temp = list(Fruit)
which will create a new list with a new memory location and hence the id(Fruit) will differ from id(temp).
Following link will give you a better understanding on how list works in python :-
http://www.python-course.eu/deep_copy.php

Correct way to iterate twice over a list?

What is the correct way to perform multiple iteration over a container? From python documentation:
Iterator - A container object (such as a list) produces a fresh new
iterator each time you pass it to the iter() function or use it in a
for loop. Attempting this with an iterator will just return the same
exhausted iterator object used in the previous iteration pass, making
it appear like an empty container.
The intention of the protocol is that once an iterator’s next() method
raises StopIteration, it will continue to do so on subsequent calls.
Implementations that do not obey this property are deemed broken.
(This constraint was added in Python 2.3; in Python 2.2, various
iterators are broken according to this rule.)
If I have this code:
slist = [1,2,3,4]
rlist = reversed(slist)
list(rlist)
#[4,3,2,1]
tuple(rlist)
#()
What would be the easiest and most correct way to iterate over 'rlist' twice?
rlist = list(reversed(slist))
Then iterate as often as you want. This trick applies more generally; whenever you need to iterate over an iterator multiple times, turn it into a list. Here's a code snippet that I keep copy-pasting into different projects for exactly this purpose:
def tosequence(it):
"""Turn iterable into a sequence, avoiding a copy if possible."""
if not isinstance(it, collections.Sequence):
it = list(it)
return it
(Sequence is the abstract type of lists, tuples and many custom list-like objects.)
I wouldn't stored the list twice, if you can not combine it to iterate once, then I would
slist = [1,2,3,4]
for element in reversed(slist):
print element # do first iteration stuff
for element in reversed(slist):
print element # do second iteration stuff
Just think of the reversed() as setting up a reverse iterator on slist. The reversed is cheap. That being said, if you only ever need it reversed, I would reverse it and just have it stored like that.
What is the correct way to perform multiple iteration over a container?
Just do it twice in a row. No problem.
What would be the easiest and most correct way to iterate over 'rlist' twice?
See, the reason that isn't working for you is that rlist isn't "a container".
Notice how
list(slist) # another copy of the list
tuple(slist) # still works!
So, the simple solution is to just ensure you have an actual container of items if you need to iterate multiple times:
rlist = list(reversed(slist)) # we store the result of the first iteration
# and then that result can be iterated over multiple times.
If you really must not store the items, try itertools.tee. But note that you won't really avoid storing the items if you need to complete one full iteration before starting the next. In the general case, storage is really unavoidable under those restrictions.
Why don't you simply reverse the original list in-place (slist.reverse()), then iterate over it as many times as you wish, and finally reverse it again to obtain the original list once again?
If this doesn't work for you, the best solution for iterating over the list in reversed order is to create a new reverse iterator every time you need to iterate
for _ in xrange(as_many_times_as_i_wish_to_iterate_this_list_in_reverse_order):
for x in reversed(slist):
do_stuff(x)

Get a pointer to a list element

I was wondering if it was possible to get a "pointer" to an element in a python list. That way, I would be able to access my element directly without needing to know my element's index. What I mean by that is that in a list, you can add elements anywhere; at the start, in the middle or even at the end, yet the individual elements aren't moved from their actual memory location. In theory, it should be possible to do something like:
myList = [1]
[1]
element = &myList[0]
element would act as a pointer here.
myList.insert(0, 0)
myList.append(2)
[0, 1, 2]
At this point, I would still be able to access the element directly even though it's index within the list has changed.
The reason I want to do this is because in my program, it would be way too tedious to keep track of every item I add to my list. Each item is generated by an object. Once in a while, the object has to update the value, yet it can't be guaranteed that it will find its item at the same index as when it was added. Having a pointer would solve the problem. I hope that makes sense.
What would be the right way to do something like that in Python?
There's no concept of pointers on python (at least that I'm aware of).
In case you are saving objects inside your list, you can simply keep a reference to that object.
In the case you are saving primitive values into your list, the approach I would take is to make a wrapper object around the value/values and keep a reference of that object to use it later without having to access the list. This way your wrapper is working as a mutable object and can be modified no matter from where you are accesing it.
An example:
class FooWrapper(object):
def __init__(self, value):
self.value = value
# save an object into a list
l = []
obj = FooWrapper(5)
l.append(obj)
# add another object, so the initial object is shifted
l.insert(0, FooWrapper(1))
# change the value of the initial object
obj.value = 3
print l[1].value # prints 3 since it's still the same reference
element = mylist[0] already works if you don't need to change the element or if element is a mutable object.
Immutable objects such as int objects in Python you can not change. Moreover, you can refer to the same object using multiple names in Python e.g., sys.getrefcount(1) is ~2000 in a fresh REPL on my system. Naturally, you don't want 1 to mean 2 all of a sudden in all these places.
If you want to change an object later then it should be mutable e.g., if mylist[0] == [1] then to change the value, you could set element[0] = 2. A custom object instead of the
[1] list could be more appropriate for a specific application.
As an alternative, you could use a dictionary (or other namespace objects such as types.SimpleNamespace) instead of the mylist list. Then to change the item, reference it by its name: mydict["a"] = 2.

Categories