Function changes list values and not variable values in Python [duplicate]

风格不统一 提交于 2019-12-27 10:27:46

问题


Let's take a simple code:

y = [1,2,3]

def plusOne(y):
    for x in range(len(y)):
        y[x] += 1
    return y

print plusOne(y), y


a = 2

def plusOne2(a):
    a += 1
    return a

print plusOne2(a), a

Values of 'y' change but value 'a' stays the same. I have already learned that it's because one is mutable and the other is not. But how to change the code so that the function doesn't change the list?

For example to do something like that (in pseudocode for simplicity):

a = [1,2,3,...,n]

function doSomething(x):
    do stuff with x
    return x

b = doSomething(a)

if someOperation(a) > someOperation(b):
    do stuff

EDIT: Sorry, but I have another question on nested lists. See this code:

def change(y):
    yN = y[:]
    for i in range(len(yN)):
        if yN[i][0] == 1:
            yN[i][0] = 0
        else:
            yN[i][0] = 1
    return yN

data1 = [[1],[1],[0],[0]]
data2 = change(data1)

Here it doesn't work. Why? Again: how to avoid this problem? I understand why it is not working: yN = y[:] copies values of y to yN, but the values are also lists, so the operation would have to be doubled for every list in list. How to do this operation with nested lists?


回答1:


Python variables contain pointers, or references, to objects. All values (even integers) are objects, and assignment changes the variable to point to a different object. It does not store a new value in the variable, it changes the variable to refer or point to a different object. For this reason many people say that Python doesn't have "variables," it has "names," and the = operation doesn't "assign a value to a variable," but rather "binds a name to an object."

In plusOne you are modifying (or "mutating") the contents of y but never change what y itself refers to. It stays pointing to the same list, the one you passed in to the function. The global variable y and the local variable y refer to the same list, so the changes are visible using either variable. Since you changed the contents of the object that was passed in, there is actually no reason to return y (in fact, returning None is what Python itself does for operations like this that modify a list "in place" -- values are returned by operations that create new objects rather than mutating existing ones).

In plusOne2 you are changing the local variable a to refer to a different integer object, 3. ("Binding the name a to the object 3.") The global variable a is not changed by this and continues to point to 2.

If you don't want to change a list passed in, make a copy of it and change that. Then your function should return the new list since it's one of those operations that creates a new object, and the new object will be lost if you don't return it. You can do this as the first line of the function: x = x[:] for example (as others have pointed out). Or, if it might be useful to have the function called either way, you can have the caller pass in x[:] if he wants a copy made.




回答2:


Create a copy of the list. Using testList = inputList[:]. See the code

>>> def plusOne(y):
        newY = y[:]
        for x in range(len(newY)):
            newY[x] += 1
        return newY

>>> y = [1, 2, 3]
>>> print plusOne(y), y
[2, 3, 4] [1, 2, 3]

Or, you can create a new list in the function

>>> def plusOne(y):
        newList = []
        for elem in y:
            newList.append(elem+1)
        return newList

You can also use a comprehension as others have pointed out.

>>> def plusOne(y):
        return [elem+1 for elem in y]



回答3:


You can pass a copy of your list, using slice notation:

print plusOne(y[:]), y

Or the better way would be to create the copy of list in the function itself, so that the caller don't have to worry about the possible modification:

def plusOne(y):
    y_copy = y[:]

and work on y_copy instead.


Or as pointed out by @abarnet in comments, you can modify the function to use list comprehension, which will create a new list altogether:

return [x + 1 for x in y]



回答4:


Just create a new list with the values you want in it and return that instead.

def plus_one(sequence):
    return [el + 1 for el in sequence]



回答5:


As others have pointed out, you should use newlist = original[:] or newlist = list(original) to copy the list if you do not want to modify the original.

def plusOne(y):
    y2 = list(y)  # copy the list over, y2 = y[:] also works
    for i, _ in enumerate(y2):
        y2[i] += 1
    return y2

However, you can acheive your desired output with a list comprehension

def plusOne(y):
    return [i+1 for i in y]

This will iterate over the values in y and create a new list by adding one to each of them




回答6:


To answer your edited question:

Copying nested data structures is called deep copying. To do this in Python, use deepcopy() within the copy module.




回答7:


You can do that by make a function and call this function by map function , map function will call the add function and give it the value after that it will print the new value like that:

def add(x):
    return x+x
print(list(map(add,[1,2,3])))

Or you can use (*range) function it is very easy to do that like that example :

print ([i+i for i in [*range (1,10)]])


来源:https://stackoverflow.com/questions/17686596/function-changes-list-values-and-not-variable-values-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!