-1

Refer to this

>>> def foo(counter=[0]):
...   counter[0] += 1
...   print("Counter is %i." % counter[0]);
... 
>>> foo()
Counter is 1.
>>> foo()
Counter is 2.
>>>

Default values are initialized only when the function is first evaluated, not each time it is executed, so you can use a list or any other mutable object to maintain static values.

Question> Why the counter can keep its updated value during different callings? Is it true that counter refers to the same memory used to store the temporary list of the default parameter so that it can refer to the same memory address and keep the updated values during the calls?

Community
  • 1
  • 1
q0987
  • 34,938
  • 69
  • 242
  • 387
  • There is no need to talk about memory locations. Thinking in terms of (mutable) objects and variable bindings is sufficent. –  Feb 29 '12 at 17:51
  • http://docs.python.org/reference/compound_stmts.html#function-definitions – q0987 Feb 29 '12 at 17:55

4 Answers4

3

The object created as the default argument becomes part of the function and persists until the function is destroyed.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
1

Default arguments are evaluated at the function definition, and not its calls. When the foo function object is created (remember, functions are first class citizens in python) its arguments, including default arguments, are all local names bound to the function object. The function object is created when the def: statement is first encountered.

In this case counter is set to a mutable list in the definition of foo(), so calling it without an argument gives the original mutable list instantiated at definition the name counter. This makes calls to foo() use and modify the original list.

shelhamer
  • 29,752
  • 2
  • 30
  • 33
1

Like all function arguments, counter is a local name defined when the function is declared. The default argument, too, is evaluated once when the function is declared.

If, when calling foo(), no value is passed for counter, that default value, the exact object instance provided at function definition time, is given the name counter. Since this is a mutable object (in this case, a list), any changes made to it remain after the function has completed.

The function contains a reference to the list in its default argument tuple, func_defaults, which prevents it from being destroyed.

kindall
  • 178,883
  • 35
  • 278
  • 309
1

First things first: if you are coding in Python: forget about "memory address" - you will never need one. Yes, there are objects, and they are placed in memory, and if you are referring to the same object, it is in the same "memory address" - but that does not matter - there could even be an implementation where objects don't have a memory address at all (just a place in a data structure, for example).

Then, when Python encounters the function body, as it is defined above, it does create a code object with the contents of the function body, and executes the function definition line - resolving any expressions inlined there and setting the results of those expressions as the default parameters for that function. There is nothing "temporary" about these objects. The expressions (in this case [0]) are evaluated, the resulting objects (in this case a Python list with a single element) are created, and assigned to a reference in the function object (a position in the functions's "func_defaults" attribute - remember that functions themselves are objects in Python.)

Whenever you call that function, if you don't pass a value to the counter parameter, it is assigned to the object recorded in the func_defaults attribute -in this case, a Python list. And it is the same Python list that was created at function parsing time.

What happens is that Python lists themselves are mutable: one can change its contents, add more elements, etc...but they are still the same list. What the code above does is exactly incrementing the element in the position 0 of the list.

You can access this list at any time in the example above by typing foo.func_defaults[0] If you want to "reset" the counter, you can just do: foo.func_defaults[0][0]=0, for example.

Of course, it is a side effect of how thigns a reimplemented in Python, and though consistent, and even docuemnted, should not be used in "real code". If you need "static variables", use a class instead of a function like the above:

class Foo(object):
  def __init__(self):
     self.counter = 0 
  def __call__(self):
    self.counter += 1 
    print("Counter is %i." % self.counter)

And on the console:

>>> foo = Foo()
>>> foo()
Counter is 1.
>>> foo()
Counter is 2.
>>> 
q0987
  • 34,938
  • 69
  • 242
  • 387
jsbueno
  • 99,910
  • 10
  • 151
  • 209