-2

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?

Yatharth Agarwal
  • 4,385
  • 2
  • 24
  • 53
Wahaha
  • 27
  • 4
  • 1
    possible duplicate of [Python and reference passing. Limitation?](http://stackoverflow.com/questions/12797749/python-and-reference-passing-limitation) –  Oct 09 '12 at 10:44
  • I also can only guess at what your question is, but it if I understand you correctly, you're wondering why your changes to l inside change() are visible outside. Read this: http://stackoverflow.com/a/986145/112308 – rodion Oct 09 '12 at 10:47

5 Answers5

2

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]
mgilson
  • 300,191
  • 65
  • 633
  • 696
1

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})
Lauritz V. Thaulow
  • 49,139
  • 12
  • 73
  • 92
  • Thanks,if I want to change a dict same to this,how can I acieve that? – Wahaha Oct 09 '12 at 10:59
  • @lazyr So dictionaries and other objects get aliased too? WHat about things like custom objects? – Yatharth Agarwal Oct 09 '12 at 11:11
  • Here's [my answer](http://stackoverflow.com/a/12798328/1292652). You answered just a second before me (hover over the timestamps) :/ – Yatharth Agarwal Oct 09 '12 at 11:12
  • @YatharthROCK This is the first time I've heard the term *aliasing* used about python variables, and it's a subject I'm very familiar with. I don't think it is an accepted term. See [this](http://stackoverflow.com/a/986145/566644) answer for a thorough explanation, and [this](http://effbot.org/zone/call-by-object.htm) more technical but semi-official summary of how python argument passing works. – Lauritz V. Thaulow Oct 09 '12 at 11:20
  • @YatharthROCK To actually answer your question, *any* python object can get "aliased", as you call it, since all variables are simply names that reference or point to the real objects. But if the object is immutable, like a string, it has no side effects so it goes unnoticed. – Lauritz V. Thaulow Oct 09 '12 at 11:28
0

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]
Yatharth Agarwal
  • 4,385
  • 2
  • 24
  • 53
0

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]
bohney
  • 1,187
  • 8
  • 12
0

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)
Rajendra
  • 340
  • 1
  • 5