0

I'm from a C++ background so this problem seems a little absurd to me: Let's suppose I have a function:

def scale(data, factor):
    for val in data:
        val *= factor

This doesn't work as intended, if I pass a list, it changes nothing, but

def scale(data, factor):
    for index, val in enumerate(data):
        data[index] *= factor

and lst = [val * factor for val in lst] works properly. How does Python handle argument passing? How do I know if the actual reference, or alias is passed?

Amir
  • 10,600
  • 9
  • 48
  • 75
fatg
  • 519
  • 7
  • 23

3 Answers3

4

if you want to mutate the list, you need to reference the elements. This version uses map (it could be written using list comprehensions)

def scale(data, factor):
    return map(lambda x : x*factor, data)

a lambda function is an anonymous function.

>>> (lambda x : x + 1) (5)
6

The x takes the place of the variable in this case 5 + 1

So in this case, we traverse the list applying the function f(x) -> x * factor to every element of the list. The original list is not mutated, but instead we return a new version.

beoliver
  • 5,579
  • 5
  • 36
  • 72
3

In python basic data types are passed by value - for example int, str, bool etc are passed by value

Derived data types like classes, enum, list, dict are passed by reference.

In your example, the problem is how you use the for loop - not the function argument. If you do:

for val in lst:
    val += 1

The values inside lst won't get updated because the val is not the same as lst[0], lst[1] and so on IF val is of the basic data types. So, even here, the val is copied by value.

Second, In your example with enumerate:
But when you loop over the enumerated list, you are using data[index] - which modifies the element in the actual list.

And finally, In your example with the generator:

lst = [val * factor for val in lst] - here the generator loops over every element and creates a new list which is again stored in lst. This is something like a = a + 2 but extended to lists.

AbdealiLoKo
  • 3,261
  • 2
  • 20
  • 36
  • could you be more specific? could you please elaborate the first function? If the list was passed by reference then why does `val *= factor` have no effect on the actual list? – fatg Jan 08 '16 at 02:59
  • @GiaPhatHa I've added more info :) – AbdealiLoKo Jan 08 '16 at 03:00
  • It is because the `for` loop creates a generator and each time it loops over a list it create an exact copy of each element? – fatg Jan 08 '16 at 03:03
  • @GiaPhatHa the for loop can be thought of simply doing a `val = lst[i++]` in the beginning of every loop. In C, this would copy the item's value for basic data types and will copy the pointer if the data type is a derived one. Python does the same – AbdealiLoKo Jan 08 '16 at 03:05
  • 1
    Passed-by-value and passed-by-reference are not particularly useful concepts here. The real issue is the distinction between assignment (to a local variable name) vs mutation (calling a method that alters a mutable object). See the answers here for more discussion: http://stackoverflow.com/questions/575196/in-python-why-can-a-function-modify-some-arguments-as-perceived-by-the-caller – FMc Jan 08 '16 at 03:31
1

This behaviour is so because the basic data types are passed by value and the derived data types like lists are passed by reference consider this

>>> x = 24

>>> x + 1
25

>>> x
24

but on the otherhand with a list

>>> y = [1, 2, 3, 4]

>>> y.remove(2)

>>> y
[1,3,4]

so you should always be careful to reassign values back when performing operations on them in the case of the basic data ypes and also be careful with datatypes that are passed by reference because you could accidentally modify a variable without knowing

danidee
  • 9,298
  • 2
  • 35
  • 55