Trouble with a function that simulates reverse attribute in python - python

I have this function to simulate reverse() in python:
My_list = [[1, 3, "s"], 2, 2, 3, 4, 2]
def listRev(list):
list=list[::-1]
print list
listRev(My_list)
print My_list
The function gives me correct reversed list but I expect when I print My_list in the last line, the code print reversed My_list not My_list itself. How can I solve my problem?

If you want to simulate reverse, you must modify the list passed as a parameter to you function, not simply use a new list in your function.
When you write list=list[::-1] the local copy of the original parameter points to a new list. You should do instead :
def rev(l):
l[:] = l[::-1]
print (l)
The usage of l[:] asks Python to replace the content of the list, and not to make the variable point to a new list.

This can be solved by returning a list from the function
My_list = [[1, 3, "s"], 2, 2, 3, 4, 2]
def listRev(list):
list=list[::-1]
print list
return list
My_list = listRev(My_list)
print My_list
This is because lists are not passed by reference in Python.
Post Comment Edit
If you want the function to be in place only for My_list even though it is very wrong and bad programming to do so, you can use this
My_list = [[1, 3, "s"], 2, 2, 3, 4, 2]
def listRev():
global My_list
My_list = My_list[::-1]
listRev()
print My_list

Related

Why is it that modifying lists in functions changes the original list, but declaring them in a function creates a new object?

I'm new to writing code, and I understand the behavior of lists to an extent. Whenever a list is modified within a function's scope, the one in global scope changes too.
For example,
def modify_list(lst):
lst.append(5)
lst = [1, 2, 3, 4]
#This output is [1, 2, 3, 4]
print(lst)
modify_list(lst)
#This output is [1, 2, 3, 4, 5] because of the function.
print(lst)
I don't understand why this example won't work:
def modify_list(lst):
lst = [1, 2, 3, 4, 5]
lst = [1, 2, 3, 4]
#Output is [1, 2, 3, 4]
print(lst)
modify_list(lst)
#Output is [1, 2, 3, 4]
print(lst)
Why doesn't lst get modified in the second example? Is it because I'm creating a new object within the function's scope? Using the global keyword works instead of passing a parameter, but I want to avoid using global unless absolutely necessary.
I'm using this in an initialization function and want to revert the list back to its original state whenever the function is called. Again, using global works, I'm just wondering why this doesn't work.
Thanks! (Sorry if I'm not good at explaining things well)
The id function comes in handy here. Basically, the id function returns an integer that is guaranteed to be unique and constant for the lifetime of whatever object it was called on. In fact, in CPython (regular Python), id returns the address of the object in memory.
So if you run your code, printing the id of lst before and after running your modify_list, you'll find that the id changes when you assign to lst but not when you append.
Calling append on lst won't change the id of lst because lists in Python are mutable, and appending simply mutates the list. But, when you assign [1, 2, 3, 4, 5] to lst, you are creating a brand-new list object and assigning it to lst. This is not a mutation, and doesn't change the original in anyway. In general in Python, you can mutate arguments within a function to modify the original copy, but assigning a new object to it is not a mutation and won't change the original copy.
In
def modify_list(lst):
lst = [1, 2, 3, 4, 5]
you make local variable lst point to a completely different object than the one you passed as the argument in modify_list(lst) call. Maybe this article will help you understand: https://medium.com/school-of-code/passing-by-assignment-in-python-7c829a2df10a

List comprehension output [duplicate]

This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 3 years ago.
I am trying to understand list comprehension by passing a list a of functions to act on list as shown in the code below.
def fun1(x):
x.append(5)
print(" In Fun 1:")
print(x)
return x
def fun2(x):
x.append(6)
return x
def fun3(x):
x.append(7)
return x
int_list = [1, 2, 3, 4]
funs_family = (fun1, fun2, fun3)
new_list = [fun(int_list) for fun in funs_family ]
print(new_list)
I am expecting the result of the new_list to be
[1,2,3,4,5] [1,2,3,4,5,6] [1,2,3,4,5,6,7]
but the actual result is
[1,2,3,4,5,6,7] [1,2,3,4,5,6,7] [1,2,3,4,5,6,7]
Can anyone explain why the actual result is different from expected result?
Return a new list in your fun_family methods and you will not see the issue:
def fun1(x):
x.append(5)
print("In Fun 1:")
print(x)
return list(x)
def fun2(x):
x.append(6)
return list(x)
def fun3(x):
x.append(7)
return list(x)
int_list = [1, 2, 3, 4]
funs_family = (fun1, fun2, fun3)
new_list = [fun(int_list) for fun in funs_family]
print(new_list)
>> [[1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6, 7]]
Here's a similar example for you to look at
int_list = [1, 2, 3, 4]
funs_family = (fun1, fun2, fun3)
new_list = [int_list for fun in funs_family]
int_list.append(5)
print(new_list)
Note that new_list does not depend on the functions at all, in fact they're never called. But what's printed is [1, 2, 3, 4, 5] three times. This is because the three lists inside new_list all point to the same list in memory. So when you change the original, it changes all of them.
Because you are appending to the list, it is actually the same object. At the end you are printing the exact same list three times. Instead, maybe what you want to do is create a new list within the function and add the int to it.
x = [1, 2, 3, 4]
x_copy = [i for i in x]
x_copy.append(5)
Each of the functions returns x.
So
new_list = [fun(int_list) for fun in funs_family ] print(new_list)
… returns three copies of x, all of which are equal to [1,2,3,4,5,6,7] once the comprehension has finished.
What you want instead are copies of x, which you can get using [:]:
new_list = [fun(int_list)[:] for fun in funs_family ]

Appending variable that is modified in a for loop doesn't work as expected [duplicate]

This question already has answers here:
List on python appending always the same value [duplicate]
(5 answers)
Closed 4 years ago.
I have this code:
lst = []
given = [1, 2, 3, 4, 5]
result = []
for item in given:
lst.append(item)
print(lst)
result.append(lst)
print(result)
My expected result is [[1], [1, 2], [1, 2, 3], ...], but displayed result is [[1, 2, 3, 4, 5], ...] with 12345 repeated 5 times. What is wrong?
lst printed is as expected, which is [1] for the first loop, [1, 2] for the second loop, and so on.
Python doesn't create copy of lst every time when you append it to result, it just inserts reference. As a result you get list with N references to same list.
To create a copy of lst you can use lst.copy(). Also list slice operator works same lst[:].
Shortened version of your code:
given = [1, 2, 3, 4, 5]
result = [given[0 : i + 1] for i in range(len(given))]
print(result)
Result:
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]]
The problem is that you are appending the list as such which is equivalent to appending the reference object to the original list. Therefore, whenever the original list is modified, the changes are reflected in the places where the reference is created, in this case in result. As you keep iterating via the for loop, all your references appended in result keep getting updated with the latest value of lst. The final result is that at the end of the for loop, you have appended 5 references to the original list lst and all of them store the latest value of lst being [1,2,3,4,5].
There are several ways to avoid this. What you need is to copy only the values. One of them is to use lst[:]. other way is to use lst.copy()
for item in given:
lst.append(item)
print(lst)
result.append(lst[:])
print (result)
# [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]]
List is a mutable data type, there is only one copy in memory for a list unless you explicitly copy it to another variable. So
result.append(lst)
just appends a reference of the real copy and all the refercences point to the same copy.
In conclusion, you should learn about mutable/immutable data types and reference count in python.
Append lst.copy() gives the right output.
lst = []
given = [1,2,3,4,5]
result = []
for item in given:
lst.append(item)
print(lst)
result.append(lst.copy())
print(result)

Appending to a list comprehension in Python returns None

This is a question out of curiosity rather than trying to use it for a practical purpose.
Consider I have the following simple example where I generate a list through list comprehension:
>>> a = [1, 2, 3]
>>> b = [2 * i for i in a]
>>> b
[2, 4, 6]
>>> b.append(a)
>>> b
[2, 4, 6, [1, 2, 3]]
However if I try and do this all in one action
>>> a = [1, 2, 3]
>>> b = [2 * i for i in a].append(a)
>>> b == None
True
The result returns None. Is there any reason why this is the case?
I would have thought that an action like this would either return an answer like in the first example or throw an error.
For reference I'm using Python 3.6.5
append only works on variables, not list literals, since it updates the list object itself and does not return the resulting list.
As #Tomalak mentioned noted, running a similar operation on a simple list also returns None
>>> [1, 2, 3].append(4) == None
True
You can use concatination + instead of append in list comprehension
In [1]: a = [1, 2, 3]
In [2]: b = [2 * i for i in a] + [a]
In [3]: b
Out[3]: [2, 4, 6, [1, 2, 3]]
#ScottMcC, methods defined on mutable objects like list, dictionary mostly perform operations on calling object and doesn't return anything.
In case of immutable object like string you may see, methods return the modified form(a different object) of the calling object. In case of list, it's different.
You can't expect the below operations on list kind of mutable objects.
s = "hello DJANGO"
s2 = s.upper()
s3 = s.lower()
print(s) # hello DJANGO
print(s2) # HELLO DJANGO
print(s3) # hello django
Now, have a look at the below examples.
list is mutable object.
Calling sort() method on list directly modified the calling object and doesn't return anything (That's why None).
Calling sorted() function doesn't alter the passing list. It creates a separate sorted list based on the passed list. As it is not a method defined on list object, it returns the new sorted list.
append() method appends item on the calling list and doesn't return anything. Once you call it, you are done with updating (appending an item) the list.
# sort() method defined on list updates the calling list
# As it updates current list, it doesn't return anything. That's why None.
a = [5, 8, 1, 2, 7]
n = a.sort()
print (a)
print(n)
print ()
# sorted() function returns a new sorted list
# It doesn't update the calling list a2
a2 = [5, 8, 1, 2, 7];
n = sorted(a2);
print (a2)
print(n)
print()
# append() is method defined on list, it updates calling list so it doesn't return anything (None)
l = []
n = l.append(34)
print(l)
print (n)
Output
[1, 2, 5, 7, 8]
None
[5, 8, 1, 2, 7]
[1, 2, 5, 7, 8]
[34]
None

How can I achieve this in python [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
Here's some code:
li=[1,2,3]
def change(l):
l[2]=10
change(li)
print li
[1, 2, 10]
But I want this:
li=[1,2,3]
def change(l):
l=[1,2,10]
change(li)
print li
[1,2,3]
For some reason,I have to change whole list in method,how can I achieve this?Anything wrong or my mistake?
When you want to change the entire list inside a method, you'll probably want to create a copy and return it:
def change(li):
new_list = li[:] #copy
new_list[2] = 10
return new_list
li = [1,2,3]
new_lst = change(li) #new_lst = [1,2,10]
If you don't want to return the list, but want to modify in place, you can use slice assignment (*warning: This is not common practice):
def change(li):
li[:] = [1,2,10]
li = [4,5,6]
change(li)
print(li) #[1, 2, 10]
Like this:
li = [1, 2, 3]
def change(l):
l[:] = [1, 2, 10]
change(li)
print li # [1, 2, 10]
The reason your approach does not work is that in python, variables are simply names that reference objects. When you write l = [1, 2, 10] you're re-binding the name l to refer to the new list you've just created, the old list is unchanged and still referred to by the global variable li.
The above code instead modifies the object pointed to by l by using slice assignment.
As mgilson indicates in his answer, you should make it very clear that the function actually modifies the passed argument in-place. If not, an unsuspecting programmer might pass it a list he intends to use as-is later, only to discover that everything in it has been lost. Giving your function a name indicating modification (like you've done) and not returning anything from it are both common indicators of such functions, like for instance random.shuffle.
To achieve the same effect for a dict, the documentation tells us that this will do:
def change_dict(d):
d.clear() # Empty dict
d.update({"a": 3, "b": 9})
When you do the following:
def change(l):
l=[1,2,10]
You are actually changing which list l points to. You need to change the list instance passed in to change. You can append to it, you can pop from it, etc and your changes will be made to the list you passed in. If you change your change function to what I have, your example will work.
def change(l):
l[:] = [1,2,10]
Using aliasing
Documentation: List Aliasing — How to Think like a Computer Scientist (2nd editon)
You can take advantage of Python's list aliasing (which is common gotcha for beginners).
When you pass li to the change function, l aliases to li, i.e., they both point to the same object in memory and changing one changes the other. But when you do l = [1, 2, 10], l is pointing to another list and you lose the aliasing magic. To resolve that, you can use the slice operation to replace to full list as so:
li = [1, 2, 3]
def change(l):
l[:] = [1, 2, 10] # doesn't make a new list
change(li) # preserves aliasing
print li # returns [1, 2, 10]
Using globals
Documentation: The global statement — Python Docs
The changes you're making to l inside the function are not applied to the li list outside. You can use a global to affect the li list you're trying to change:
li = [1, 2, 3]
def change():
global li
li = [1, 2, 10] # use this
li[2] = 10 # or this, up to you
change()
print li # returns [1, 2, 10]
In this code the l is work as common object to whole so you can check by the id() that provide the object memory location.
so that's why you got the last output [1,2,10]
l = [1, 2, 3]
print id(l)
def change(l):
l[:] = [1, 2, 10]
print id(l)
change(l)
print l
print id(l)
if you want your desired output than you use the deepcopy that provide the diffident object to function.
from copy import deepcopy
l = [1, 2, 3]
print id(l)
def change(l):
l[:] = [1, 2, 10]
print id(l)
change(deepcopy(l))
print l
print id(l)

Categories