5

I have a question about the scope in python functions. I've included an example that demonstrates the issue I am having.

fun0 redefines the first entry in varible c's list. This change is reflected outside of fun0, even when I don't return any values from fun0.

fun1 redefines the variable c entirely, but the change isn't reflected outside of fun1. Similarly, fun2 redefines c, and the change isn't reflected outside of fun2.

My question is, why does fun0 modify val3 in the main function, while fun1 and fun2 don't modify val4 and val7, respectively?

def fun0(a, b, c):
    c[0] = a[0] + b[0]
    return

def fun1(a, b, c):
    c = a[0] + b[0]
    return

def fun2(a, b, c):
    c = a + b
    return

def main():
    val1 = ['one']
    val2 = ['two']
    val3 = ['']
    fun0(val1, val2, val3)
    print val3

    val4 = []
    fun1(val1, val2, val4)
    print val4

    val5 = 1
    val6 = 1
    val7 = 0
    fun2(val5, val6, val7)
    print val7
    return

if __name__=='__main__':
    main()
wzaylor
  • 195
  • 2
  • 9
  • 4
    Lists are mutable, you can modify their elements and it affects everyone with a reference. When you re-assign it, you're creating a new reference. – byxor Jul 07 '17 at 13:23
  • It's quite weird how I refer to other pieces of code as "everyone". Maybe humanising code is my way of understanding it better. – byxor Jul 07 '17 at 13:24

5 Answers5

2

It has to do with the way lists and variables are stored. You can modify a list within a function because they are mutable objects. However, if you do c = a[0] + b[0], you are creating a local variable within fun1 and fun2, which stays within the function scope.

Right leg
  • 16,080
  • 7
  • 48
  • 81
Cary Shindell
  • 1,336
  • 8
  • 25
1

My question is, why does fun0 modify val3 in the main function, while fun1 and fun2 don't modify val4 and val7, respectively?

This is because of the way Python passes arguments.

When you pass arguments to a function, Python passes them by assignment.This means it binds the argument references to the parameter names. That's the only way the parameter and argument names are related. In your first example, you simply passed a reference to val3, and since both val3 and c were referring to the same list object, the changes were reflected across both names.

However, when you reassigned c inside of your functions, you replaced the reference to the lists c was holding and reassigned c a new reference.

Christian Dean
  • 22,138
  • 7
  • 54
  • 87
  • As far as scope goes, the functions in my example have access to the input parameters, local and global variables. If an argument is mutable, then a function can change it's values by reference, and in turn, those changes happen outside the scope of the function (correct me if I'm wrong, or inaccurate). – wzaylor Jul 07 '17 at 14:04
  • Yes, everything you said is pretty much correct. The only comment I would make is that variables are not global by default. [See here for more details](https://stackoverflow.com/questions/10588317/python-function-global-variables). – Christian Dean Jul 07 '17 at 14:31
  • Ok, so when I'm reviewing code, I'll also keep in mind if an input parameter is mutable or not. Thank you for your help! – wzaylor Jul 07 '17 at 14:36
  • Glad to help @shoe02. – Christian Dean Jul 07 '17 at 14:36
1

Technically when you pass an argument to a function you are passing a copy, not the original. Now there is a difference between passing a list and passing variable. A list holds a reference to other object while a variable holds a value. Therefore when passing a list even if it gets copied it still is referencing the same objects and can be changed within the function, but when passing a variable the copied object has nothing to do with the original. Hope this make sense.

MotKohn
  • 3,485
  • 1
  • 24
  • 41
0

What Cary Shindell is absolutely correct. Furthermore, if you DID want to save the updated values, here is how you can change your code to do that, I changed the print statements since I tested this in python 3.6.1. Good question though.

def main():
    val1 = ['one']
    val2 = ['two']
    val3 = ['']
    fun0(val1, val2, val3)
    print(val3)

    val4 = []
    val4 = fun1(val1, val2, val4)
    print(val4)

    val5 = 1
    val6 = 1
    val7 = 0
    val7 = fun2(val5, val6, val7)
    print(val7)
    return
J0hn
  • 570
  • 4
  • 19
0

as said before, lists are mutable. If you pass a list and modify it, you do it everywhere. This applies for all lists in fun0.

in fun2 you pass no lists and and therefore c is calculated within fun2, but not available on the outside.

I'm not sure about fun1, but i think you are overwriting c with a new variable that is not available on the outside as in fun2.

Zian
  • 559
  • 4
  • 16