A word to newbie Python hackers
Python passes arguments by reference. What this means is that, if the argument
is mutable, you can pass arguments to be modified. For example,
def add_blee(arg):
arg.append("blee")
Now, calling add_blee with a list argument will modify that list. Try
this_list = []
add_blee(this_list)
print this_list
Now, if that works as expected, what about this?
def increment_list(input_list):
input_list = [x + 1 for x in input_list]
This behaviour has tripped up many python newbies, especially those with a C
background. Consider what happens. A new list is created. Then, it is bound
(via =) to the name input_list. This binding occurs in the local scope. So,
within the function, the name input_list now refers to the newly created list.
However, the original object hasn't been modified at all, and input_list in the
caller's scope still refers to that object.
So, now that we know how it really works, how do we get Python to do what we
mean? The key is to avoid *unmodified assignment* on any arguments you want to
be modified in the caller's scope. The modification can be, for example, an
attribute or index lookup. The following code modifies the object represented
by a, rather than rebinding the name a:
a[3] = 7
a.someAttribute = 5
With this in mind, we could rewrite increment_list:
def increment_list(input_list):
input_list[:] = [x + 1 for x in input_list]
However, one wonderful feature of Python is that functions can return multiple
arguments in the form of a tuple. Since all arguments are passed by reference,
there is no performance reason to pass arguments to be modified (usually). So,
the following is a much nicer, much more pythonian function:
def increment_list(input_list):
return [x + 1 for x in input_list]
Please, don't hesitate to return tuples. Modifying arguments like you would in
C is usually a bad idea (with the obvious exception being a method modifying
a class instance). In fact, doing anything like you would in C is a bad idea.
In fact, doing anything is probably a bad idea. I'm going back to bed.
---
I thought I should mention that a few people have asked how to use globals
recently. Firstly, if you really need to use globals, make sure anyone using
your function will be aware of what side effects it has. See if you can't
encapsulate that state in an object.
Secondly, globals are only global at module level. This is good, because it
means a module won't be able to mess about with your own namespace. Don't try
setting script variables silently from a module function- either make the user
input the variables to be changed, or better still, return the values.
Thirdly, the keyword 'global' does nothing when used in the global scope. It is
used within the function, to specify which variables you want to be treated to
always have global binding. For example,
blee = 7
def foo():
global blee
blee = [0,5]
Note that a variable does not have to be defined global to be read or even
modified from within a function- python is properly scoped. However, unmodified
assignment needs to be protected with a global statement. For example,
sevens = []
def add_a_seven():
sevens.append(7)
works as expected, but this (horrible) function requires a global statement to
behave as expected:
def increment_vlist():
global vlist
vlist = [x + 1 for x in vlist]
Again I should suggest that anywhere you need to maintain or share state, you
should be using a class. For example, the sevens example could be:
class sevens:
def __init__(self,initial_value = []):
self.value = initial_value
def add_a_seven(self):
self.value.append(7)
A Wavefunction in Wonderland