Parameter Passing
"call by value" and "call by name"
The most common evaluation strategy when passing arguments to a function has been call by value and call by reference:
- Call by Value
The most common strategy is the call-by-value evaluation, sometimes also called pass-by-value. This strategy is used in C and C++ for example. In call-by-value, the argument expression is evaluated, and the result of this evaluation is bound to the corresponding variable in the function. So, if the expression is a variable, a local copy of its value will be used, i.e. the variable in the caller's scope will be unchanged when the function returns. - Call by Reference
In call-by-reference evaluation, which is also known as pass-by-reference, a function gets an implicit reference to the argument, rather than a copy of its value. As a consequence, the function can modify the argument, i.e. the value of the variable in the caller's scope can be changed. The advantage of call-by-reference consists in the advantage of greater time- and space-efficiency, because arguments do not need to be copied. On the other hand this harbours the disadvantage that variables can be "accidentally" changed in a function call. So special care has to be taken to "protect" the values, which shouldn't be changed.
Many programming language support call-by-reference, like C or C++, but Perl uses it as default.
and what about Python?
There are books which call the strategy of Python call-by-value and others call it call-by-reference. You may ask yourself, what is right.
Humpty Dumpty supplies the explanation:
--- "When I use a word," Humpty Dumpty said, in a rather a scornful tone, "it means just what I choose it to mean - neither more nor less."
--- "The question is," said Alice, "whether you can make words mean so many different things."
--- "The question is," said Humpty Dumpty, "which is to be master - that's all."
Lewis Carroll, Through the Looking-Glass
To come back to our initial question what is used in Python: The authors who call the mechanism call-by-value and those who call it call-by-reference are stretching the definitions until they fit.
Correctly speaking, Python uses a mechanism, which is known as "Call-by-Object", sometimes also called "Call by Object Reference" or "Call by Sharing".
If you pass immutable arguments like integers, strings or tuples to a function, the passing acts
like call-by-value. The object reference is passed to the function parameters. They can't be
changed within the function, because they can't be changed at all,
i.e. they are immutable.
It's different, if we pass mutable arguments. They are also passed by object reference, but they can be changed
in place in the function. If we pass a list to a function, we have to consider two cases: Elements of a list can be
changed in place, i.e. the list will be changed even in the caller's scope. If a new list is assigned to the name,
the old list will not be affected, i.e. the list in the caller's scope will remain untouched.
First, let's have a look at the integer variables. The parameter inside of the function remains a reference to the arguments variable, as long as the parameter is not changed. As soon as a new value will be assigned to it, Python creates a separate local variable. The caller's variable will not be changed this way:
def ref_demo(x):
print "x=",x," id=",id(x)
x=42
print "x=",x," id=",id(x)
In the example above, we used the id() function, which takes an object as a parameter.
id(obj) returns the "identity" of the object "obj". This identity, the return value of
the function, is an integer which is unique and constant for this object during its lifetime.
Two different objects with non-overlapping lifetimes may have the same id() value.
If you call the function ref_demo() - like we do in the green block further down - we can check with the id() function what happens to x. We can see that in the main scope, x has the identity 41902552. In the first print statement of the ref_demo() function, the x from the main scope is used, because we can see that we get the same identity. After we have assigned the value 42 to x, x gets a new identity 41903752, i.e. a separate memory location from the global x. So, when we are back in the main scope x has still the original value 9.
This means, that Python initially behaves like call-by-reference, but as soon as we are changing the value of such a variable, Python "switches" to call-by-value.
>>> x = 9 >>> id(x) 41902552 >>> ref_demo(x) x= 9 id= 41902552 x= 42 id= 41903752 >>> id(x) 41902552 >>>
Side effects
A function is said to have a side effect if, in addition to producing a value, it modifies the caller's environment in other ways. For example, a function might modify a global or static variable, modify one of its arguments, raise an exception, write data to a display or file and so on.In many cases these side effects are wanted, i.e. they are part of the functions specification. But in other cases, they are not wanted , they are hidden side effects. In this chapter we are only interested in the side effects, which change global variables, which have been passed as arguments to a function.
Let's assume, we are passing a list to a function. We expect, that the function is not changing this list. First let's have a look at a function which has no side effects. As a new list is assigned to the parameter list in func1(), a new memory location is created for list and list becomes a local variable.
>>> def func1(list): ... print list ... list = [47,11] ... print list ... >>> fib = [0,1,1,2,3,5,8] >>> func1(fib) [0, 1, 1, 2, 3, 5, 8] [47, 11] >>> print fib [0, 1, 1, 2, 3, 5, 8] >>>This changes drastically, if we include something in the list by using +=. To show this, we have a different function func2() in the following example:
>>> def func2(list): ... print list ... list += [47,11] ... print list ... >>> fib = [0,1,1,2,3,5,8] >>> func2(fib) [0, 1, 1, 2, 3, 5, 8] [0, 1, 1, 2, 3, 5, 8, 47, 11] >>> print fib [0, 1, 1, 2, 3, 5, 8, 47, 11] >>>The user of the function can prevent this by passing a copy to the function. In this case a shallow copy is sufficient:
>>> def func2(list): ... print list ... list += [47,11] ... print list ... >>> fib = [0,1,1,2,3,5,8] >>> func2(fib[:]) [0, 1, 1, 2, 3, 5, 8] [0, 1, 1, 2, 3, 5, 8, 47, 11] >>> print fib [0, 1, 1, 2, 3, 5, 8] >>>
Command Line Arguments
It's possible to write Python scripts using command line arguments. If you call a Python script from a shell, the arguments are placed after the script name. The arguments are separated by spaces. Inside of the script these argumetns are accessible through the list variable sys.argv. The name of the script is included in this list sys.argv[0]. sys.argv[1] contains the first parameter, sys.argv[2] the second and so on.The following script (arguments.py) prints all arguments:
# Module sys has to be imported:
import sys
# Iteration over all arguments:
for eachArg in sys.argv:
print eachArg
Example call to this script:
python argumente.py python course for beginnersThis call creates the following output:
argumente.py python course for beginners