开发者

Hole-in-scope, dead code or why such output?

Code

def change1(list1):
        list1[1] = list1[1] + 5

def change2(number):
  开发者_开发百科      number = number + 2

def main():
        numbers = [4, 8, 12]
        change1(numbers)
        variable = 15
        change2(variable)
        i = 0
        while i < 3:
                print numbers[i]
                i += 1
        print variable

main()

When I read it, I thought it will output 4 8 12 15 but it outputs 4 13 12 15. I can see here that Python deals with integer and lists differently, I assumed that the last thing is impossible without global. I cannot understand the output, in such case, why would it not output 4 13 12 17?

You can see here almost identical code with different types and different reference:

$ python test2.py 
4
13
12
15
$ python test3.py 
4
13
12
17
$ cat test2.py test3.py 

Pass-by-reference examples

test2.py: pass-by-reference and mutable data type -example. Table/list is not enough to affect the local variable in main, you need the Reference!

def change1(list1):
    list1[1] = list1[1] + 5

def change2(number):
    number = [x+2 for x in number]

def main():
    numbers = [4, 8, 12]
    change1(numbers)
    variable = [15]
    change2(variable)
    i = 0
    while i < 3:
        print numbers[i]
        i += 1
    print variable[0]

main()

test3.py: pass-by-reference example, changing a mutable data type list/table outside the main function

def change1(list1):
    list1[1] = list1[1] + 5

def change2(number):
    number[0] += 2

def main():
    numbers = [4, 8, 12]
    change1(numbers)
    variable = [15]
    change2(variable)
    i = 0
    while i < 3:
        print numbers[i]
        i += 1
    print variable[0]

main()

pass-by-value examples

test4.py: trying to find an example with pass-by-value, why it does not work?

$ cat test4.py 

# Not yet a pass-by-value example!

global variable
variable = [15]

def change1(list1):
    list1[1] = list1[1] + 5

def change2(number):
    number = [x+2 for x in number] 

def main():
    numbers = [4, 8, 12]
    change1(numbers)
    #variable = 15
    change2(variable)
    i = 0
    while i < 3:
        print numbers[i]
        i += 1
    print variable[0]

main()
$ python test4.py 
4
13
12
15   # I expected 17! Why no 17?


def change1(list1):
        # `list1[1] =` means you are changing the object passed in
        list1[1] = list1[1] + 5

def change2(number):
        # `number = ` means you create a **new local variable**, number, 
        # based on the `number`you passed in
        number = [x+2 for x in number]

So if you want to change existing objects, you have to referene them in some way, for example in

def change3(number):
    # `number[:]` is the whole existing list and you overwrite it
    number[:] = [x+2 for x in number]

Note the [ .. ] when changing a list.


Python parameters are passed by reference. You mutating only one object in change1.

However, numerical values and Strings are all immutable. You cannot change the value of a passed in immutable and see that value change in the caller. Dictionaries and Lists on the other hand are mutable, and changes made to them by a called function will be preserved when the function returns.

More: http://www.penzilla.net/tutorials/python/functions/


The definitive answer is that Python is actually "call by sharing", also known as "call by object" or "call by object reference".

This has been extensively discussed before. From that article:

From time to time, people who’ve read a little CS but not a lot CS (or too much of just one kind of CS) pop up on comp.lang.python and waste a lot of energy trying to tell everyone that Python’s using some calling model that it doesn’t really use. It always turns out that they don’t really understand Python’s model, and quite often, they don’t understand their favourite model either.

But nevermind, the only thing you need to know is that Python’s model is neither “call by value” nor “call by reference” (because any attempt to use those terms for Python requires you to use non-standard definitions of the words “-value” and “-reference”). The most accurate description is CLU’s “call by object” or “call by sharing“. Or, if you prefer, “call by object reference“.

You should also read this, if you haven’t done so already.

Python's semantics are most similar to the semantics of the language CLU. The CLU Reference Manual by Liskov et al describes the semantics like this:

"We call the argument passing technique call by sharing, because the argument objects are shared between the caller and the called routine. This technique does not correspond to most traditional argument passing techniques (it is similar to argument passing in LISP). In particular it is not call by value because mutations of arguments per- formed by the called routine will be visible to the caller. And it is not call by reference because access is not given to the variables of the caller, but merely to certain objects."


In change1 you exchange the value in the list with value + 5.
In change2 you add 5 to number. The result is a new object and is not just applied to the passed variable. If you come from C++: No there is no int& var in Python.

You get the expected result when doing this:

def change2(number):
    return number + 5

variable = 15
variable = change2(variable)

If you still don't want to return a value, you could create a MutableInt class.

class MutableInt(object):

    def __init__(self, value = 0):
        self._value = int(value)

    def __add__(self, other):
        self._value += int(other)
        return self

    def __sub__(self, other):
        self._value -= int(other)
        return self

    ...


All the examples show call-by-value. Python only has call-by-value. There is no call-by-reference. All values in python are references (it is not possible to have an "object" as the value). Hence it is references that are copied when passed to the function. Lists are mutable, so it is possible to mutate its contents through a shared reference. In change2 you are reassigning a local variable to point to another object, which, like all assignments to local variables, has no effect on any calling scope, since it is call-by-value.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜