Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Checking default arguments

0 views
Skip to first unread message

Igor V. Rafienko

unread,
Feb 2, 2007, 1:30:53 PM2/2/07
to

Hi,


I was wondering whether it was possible to find out which parameter
value is being used: the default argument or the user-supplied one.
That is:

def foo(x, y="bar"):
# how to figure out whether the value of y is
# the default argument, or user-supplied?

foo(1, "bar") => user-supplied
foo(1) => default

{}.pop seems to be able to make this dictinction.

I've checked the inspect module, but nothing obvious jumped at me. Any
hints?

Thanks,

ivr
--
"...but it's HDTV -- it's got a better resolution than the real world."
-- Fry, "When aliens attack"

Neil Cerutti

unread,
Feb 2, 2007, 2:14:28 PM2/2/07
to
On 2007-02-02, Igor V. Rafienko <ig...@ifi.uio.no> wrote:
> Hi,
>
> I was wondering whether it was possible to find out which
> parameter value is being used: the default argument or the
> user-supplied one. That is:
>
> def foo(x, y="bar"):
> # how to figure out whether the value of y is
> # the default argument, or user-supplied?
>
> foo(1, "bar") => user-supplied
> foo(1) => default
>
> {}.pop seems to be able to make this dictinction.

You can fake it (this may be how dict.pop work) by not providing
defaults, but using positional arguments.

Here's a silly example, which returns a tuple if the user
supplies the second argument, and a list otherwise.

def foo(x, *args):
if len(args) == 0:
y_provided = True
y = "bar"
else:
y_provided = False
y = args[0]
if y_provided:
return (x, y)
else:
return [x, y]

--
Neil Cerutti
Wonderful bargains for men with 16 and 17 necks --sign at clothing store

Steven Bethard

unread,
Feb 2, 2007, 2:23:36 PM2/2/07
to
Igor V. Rafienko wrote:
> I was wondering whether it was possible to find out which parameter
> value is being used: the default argument or the user-supplied one.
> That is:
>
> def foo(x, y="bar"):
> # how to figure out whether the value of y is
> # the default argument, or user-supplied?
>
> foo(1, "bar") => user-supplied
> foo(1) => default

Why are you trying to make this distinction? That is, why should passing
in "bar" be any different from getting the default value "bar"

You could do something like::

>>> def foo(x='b a r'):
... if x is foo.func_defaults[0]:
... print 'default'
... else:
... print 'supplied'
...
>>> foo('b a r')
supplied
>>> foo()
default

But that won't really work if your default value gets interned by
Python, like small integers or identifier-like string literals::

>>> def foo(x='bar'):
... if x is foo.func_defaults[0]:
... print 'default'
... else:
... print 'supplied'
...
>>> bar = ''.join(chr(i) for i in [98, 97, 114])
>>> foo(bar)
supplied
>>> foo('bar')
default


STeVe

Gabriel Genellina

unread,
Feb 2, 2007, 3:56:47 PM2/2/07
to pytho...@python.org
En Fri, 02 Feb 2007 15:30:53 -0300, Igor V. Rafienko <ig...@ifi.uio.no>
escribió:

>> I was wondering whether it was possible to find out which parameter
> value is being used: the default argument or the user-supplied one.
> That is:
>
> def foo(x, y="bar"):
> # how to figure out whether the value of y is
> # the default argument, or user-supplied?
>
> foo(1, "bar") => user-supplied
> foo(1) => default

You can use None as a flag:

def foo(x, y=None):
if y is None: y="bar"
...

If None is valid, use your own flag:

_marker=object()
def foo(x, y=_marker):
if y is _marker: y="bar"
...

--
Gabriel Genellina

Ben Finney

unread,
Feb 3, 2007, 12:18:39 AM2/3/07
to pytho...@python.org
ig...@ifi.uio.no (Igor V. Rafienko) writes:

> I was wondering whether it was possible to find out which parameter
> value is being used: the default argument or the user-supplied one.
> That is:
>
> def foo(x, y="bar"):
> # how to figure out whether the value of y is
> # the default argument, or user-supplied?

If it doesn't make a difference, use "bar".

If it *does* make a difference, use a unique object:

===== wombat.py =====
Y_DEFAULT = object()

def foo(x, y=Y_DEFAULT):
if y is Y_DEFAULT:
# no value for y was supplied
else:
# y value was specified by caller
=====

===== fnord.py =====

import wombat

foo("spam") # 'y' argument gets unique default object
foo("spam", "eggs") # 'y' argument is supplied by caller
=====

By using a unique object, and comparing on "is", you make it clear
that this is never going to match any other value that is created
elsewhere in the program.

Of course, the "consenting adults" maxim applies: if a perverse user
wnats to call 'foo("spam", wombat.Y_DEFAULT)' this doesn't prevent it,
but then they've explicitly asked for it.

--
\ "A lot of people are afraid of heights. Not me, I'm afraid of |
`\ widths." -- Steven Wright |
_o__) |
Ben Finney

George Sakkis

unread,
Feb 3, 2007, 12:56:28 PM2/3/07
to
On Feb 2, 1:30 pm, i...@ifi.uio.no (Igor V. Rafienko) wrote:
> Hi,
>
> I was wondering whether it was possible to find out which parameter
> value is being used: the default argument or the user-supplied one.
> That is:
>
> def foo(x, y="bar"):
> # how to figure out whether the value of y is
> # the default argument, or user-supplied?
>
> foo(1, "bar") => user-supplied
> foo(1) => default
>
> {}.pop seems to be able to make this dictinction.
>
> I've checked the inspect module, but nothing obvious jumped at me. Any
> hints?

I don't know why you might want to distinguish between the two in
practice (the unique object idea mentioned in other posts should
handle most uses cases), but if you insist, here's one way to do it:

import inspect

def determine_supplied_args(func):
varnames,_,_,defaults = inspect.getargspec(func)
num_varnames = len(varnames); num_defaults = len(defaults)
def wrapper(*args, **kwds):
max_defaults = min(num_defaults, num_varnames-len(args))
supplied_args = dict(zip(varnames,args))
default_args = {}
if max_defaults > 0:
for var,default in zip(varnames[-max_defaults:], defaults[-
max_defaults:]):
# if passed as keyword argument, don't use the default
if var in kwds:
supplied_args[var] = kwds[var]
else:
default_args[var] = default
wrapper._supplied_args = supplied_args
wrapper._default_args = default_args
return func(*args, **kwds)
return wrapper


@determine_supplied_args
def f(x, y='bar', z=None, *args, **kwds):
print "Supplied:", f._supplied_args
print "Default:", f._default_args


>>> f(1)
Supplied: {'x': 1}
Default: {'y': 'bar', 'z': None}
>>> f(1, 'bar')
Supplied: {'y': 'bar', 'x': 1}
Default: {'z': None}
>>> f(1, y='bar')
Supplied: {'y': 'bar', 'x': 1}
Default: {'z': None}
>>> f(1, z=None)
Supplied: {'x': 1, 'z': None}
Default: {'y': 'bar'}
>>> f(1, 'bar', None)
Supplied: {'y': 'bar', 'x': 1, 'z': None}
Default: {}
>>> f(1, 'bar', z=None)
Supplied: {'y': 'bar', 'x': 1, 'z': None}
Default: {}
>>> f(1, z=None, y='bar')
Supplied: {'y': 'bar', 'x': 1, 'z': None}
Default: {}


Regards,
George

Igor V. Rafienko

unread,
Feb 5, 2007, 9:09:34 PM2/5/07
to
[ George Sakkis ]

First of all, thanks to everyone who replied.

[ ... ]

> I don't know why you might want to distinguish between the two in
> practice (the unique object idea mentioned in other posts should
> handle most uses cases), but if you insist, here's one way to do it:


There is no actual code situation where it happens. I was simply
intrigued by the problem and could not find an answer.

Since {}.pop behaves this way, I checked the C code. Apparently, it
*is* quite easy from C, since PyArg_UnpackTuple and clever initial
values (NULL vs. Py_None) make all the difference.

The inspect-suggestion is, unfortunately, a bit more difficult to
comprehend than I initially hoped for :)

Thanks for all the suggestions

[ ... ]

0 new messages