my take on mutable default arguments

i thought this code be solved nicely with a decorator... it needs some more work, but it would make a good cookbook recipe: .>>> from copy import deepcopy. .>>> .>>> def defaults(**kwdefs): ... def deco(func): ... def wrapper(*args, **kwargs): ... for k,v in kwdefs.iteritems(): ... if k not in kwargs: ... kwargs[k] = deepcopy(v) ... return func(*args, **kwargs) ... return wrapper ... return deco ... .>>> @defaults(x = []) ... def foo(a, x): ... x.append(a) ... print x ... .>>> foo(5) [5] .>>> foo(5) [5] .>>> foo(5) [5] maybe it should be done by copying func_defaults... then it could be written as @copydefaults def f(a, b = 5, c = []): ... -tomer

i think this is better: .>>> from copy import deepcopy .>>> .>>> def copydefaults(func): ... defaults = func.func_defaults ... def wrapper(*args, **kwargs): ... func.func_defaults = deepcopy(defaults) ... return func(*args, **kwargs) ... return wrapper ... .>>> @copydefaults ... def f(a, x = []): ... x.append(a) ... print x ... .>>> .>>> f(1) [1] .>>> f(2) [2] .>>> f(3) [3] -tomer

On 1/28/07, Chris Rebert <cvrebert@gmail.com> wrote:
first, please refer to my second version (@copydefaults). second, theoretically all objects are copyable, as long as they define __copy__ or __deepcopy__. it's up to you. third, you'll use this decorator only when you want "fresh copies" of the default values... not "just for fun", and since you'll need these fresh copies anyhow, creating the value by hand ("if x is None: x = []") will not yield a dramatic performance gain, as most default objects are "primitive" anyway (empty lists and stuff, not dicts of 10000 keys) the main issue is readability and auto-documentation. using "def f(x = [])" is better documenting than "def f(x = None)" and fourth, my apologies, but such a decorator already existed (and even with the same semantics that i used): http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/303440 -tomer

"tomer filiba" <tomerfiliba@gmail.com> escribió en el mensaje news:1d85506f0701271422j5650fcdajefb0ab33215c1de3@mail.gmail.com...
No, I think he's pointing another problem. For function calls, subscripts or attributes, it's important *when* you execute it. By example: def foo1(x, prompt=sys.ps2): print prompt,x def foo2(x, prompt=None): if prompt is None: prompt=sys.ps2 print prompt,x @copydefaults def foo3(x, prompt=sys.ps2): print prompt,x If the intent of the author is "use sys.ps2 unless I pass an explicit prompt" neither foo1 nor foo3 will work if sys.ps2 is changed.
second, theoretically all objects are copyable, as long as they define __copy__ or __deepcopy__. it's up to you.
Not all objects are copyable, or not as one would expect: @copydefaults def foo4(x, outfile=sys.stdout): "Use sys.stdout unless an explicit outfile is given" outfile.write(x) foo4 doesn't even work, it raises: ValueError: I/O operation on closed file. Even if you manage to special-case files, or "fix" how they are copied, it won't work as intended because sys.stdout could have been reassigned after the function was defined. So you have to use the old idiom anyway: def foo5(x, outfile=None): if outfile is None: outfile = sys.stdout outfile.write(x)
third, you'll use this decorator only when you want "fresh copies" of the default values... not "just for fun", [...]
Some way to say "I want this default argument to be re-evaluated when I don't provide an explicit value" would be useful, but this proposal does not help on this. -- Gabriel Genellina

i think this is better: .>>> from copy import deepcopy .>>> .>>> def copydefaults(func): ... defaults = func.func_defaults ... def wrapper(*args, **kwargs): ... func.func_defaults = deepcopy(defaults) ... return func(*args, **kwargs) ... return wrapper ... .>>> @copydefaults ... def f(a, x = []): ... x.append(a) ... print x ... .>>> .>>> f(1) [1] .>>> f(2) [2] .>>> f(3) [3] -tomer

On 1/28/07, Chris Rebert <cvrebert@gmail.com> wrote:
first, please refer to my second version (@copydefaults). second, theoretically all objects are copyable, as long as they define __copy__ or __deepcopy__. it's up to you. third, you'll use this decorator only when you want "fresh copies" of the default values... not "just for fun", and since you'll need these fresh copies anyhow, creating the value by hand ("if x is None: x = []") will not yield a dramatic performance gain, as most default objects are "primitive" anyway (empty lists and stuff, not dicts of 10000 keys) the main issue is readability and auto-documentation. using "def f(x = [])" is better documenting than "def f(x = None)" and fourth, my apologies, but such a decorator already existed (and even with the same semantics that i used): http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/303440 -tomer

"tomer filiba" <tomerfiliba@gmail.com> escribió en el mensaje news:1d85506f0701271422j5650fcdajefb0ab33215c1de3@mail.gmail.com...
No, I think he's pointing another problem. For function calls, subscripts or attributes, it's important *when* you execute it. By example: def foo1(x, prompt=sys.ps2): print prompt,x def foo2(x, prompt=None): if prompt is None: prompt=sys.ps2 print prompt,x @copydefaults def foo3(x, prompt=sys.ps2): print prompt,x If the intent of the author is "use sys.ps2 unless I pass an explicit prompt" neither foo1 nor foo3 will work if sys.ps2 is changed.
second, theoretically all objects are copyable, as long as they define __copy__ or __deepcopy__. it's up to you.
Not all objects are copyable, or not as one would expect: @copydefaults def foo4(x, outfile=sys.stdout): "Use sys.stdout unless an explicit outfile is given" outfile.write(x) foo4 doesn't even work, it raises: ValueError: I/O operation on closed file. Even if you manage to special-case files, or "fix" how they are copied, it won't work as intended because sys.stdout could have been reassigned after the function was defined. So you have to use the old idiom anyway: def foo5(x, outfile=None): if outfile is None: outfile = sys.stdout outfile.write(x)
third, you'll use this decorator only when you want "fresh copies" of the default values... not "just for fun", [...]
Some way to say "I want this default argument to be re-evaluated when I don't provide an explicit value" would be useful, but this proposal does not help on this. -- Gabriel Genellina
participants (3)
-
Chris Rebert
-
Gabriel Genellina
-
tomer filiba