question od default args

Bengt Richter bokr at oz.net
Thu Jun 27 01:06:22 CEST 2002


On Wed, 26 Jun 2002 18:17:28 GMT, "Fredrik Lundh" <fredrik at pythonware.com> wrote:

>Gonçalo Rodrigues wrote:
>
>> When I want default args I usually do
>>
>> def (arg = None):
>>     ...
>>
>> But what if I want to make None a reasonable argument too? That is, I
>> want to know if the user has in fact passed an argument, and if not to
>> do something special, with None (or whatever object) being a reasonable
>> arg to be passed.
>
>hmm.  wasn't this just discussed in some thread on a
>newsgroup near you?
>
>three alternatives:
>
>    __UNDEF__ = [] # guaranteed to have a unique id
>
>    def myfunc(arg=__UNDEF__):
>        if arg is __UNDEF__:
>            print "no argument"
>        else:
>            ...
>
>or, quite ugly:
>
>    def myfunc(*args):
>        if not args:
>            print "no argument"
>        else:
>            (arg,) = args
>            ...
>
>or, preferred:
>
>    come up with a better design; good designs allow people to
>    explicitly pass in a "use the default value" argument value...
>
You could also just use keyword args to override one or more defaults by name, e.g.,

 >>> def myfunc(arg, **kw):
 ...     if len(kw)==1 and kw.has_key('default_name'):
 ...         use_this = kw['default_name']
 ...     elif len(kw): raise TypeError,'myfunc received bad or too many kw args'
 ...     else: use_this = '<default value>'
 ...     print 'Using %s and %s' % (`arg`, `use_this` )
 ...
 >>> myfunc('1st arg')
 Using '1st arg' and '<default value>'
 >>> myfunc('1st arg',default_name='override')
 Using '1st arg' and 'override'
 >>> myfunc('1st arg','override')
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 TypeError: myfunc() takes exactly 1 argument (2 given)
 >>> myfunc('1st arg',default_name='override',bad=123)
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
   File "<stdin>", line 4, in myfunc
 TypeError: myfunc received bad or too many kw args
 >>> myfunc('1st arg',wrong_name='override')
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
   File "<stdin>", line 4, in myfunc
 TypeError: myfunc received bad or too many kw args

And, just to throw something else into the picture ... ;-)

 >>> def foo(x='arbitrary default'):
 ...     if x is foo.func_defaults[0]: print 'Using default object'
 ...     else: print `x`, 'is not the same object as the default'
 ...     if x == foo.func_defaults[0]: print 'Using object equal to default.'
 ...
 >>> foo()
 Using default object
 Using object equal to default.
 >>> foo('arbitrary default')
 'arbitrary default' is not the same object as the default
 Using object equal to default.
 >>> foo(None)
 None is not the same object as the default

Of course, this doesn't differentiate well between passed and default args if
they are in the magically shared common ints and strings, but the distinction
can be interesting and maybe useful (and a potential source of bugs -- watch
out for default mutables ;-) Here's a contrivance to tag things to you can see effects:

 >>> def baz(d={}):
 ...     baz.c = baz.__dict__.get('c',{})
 ...     if d is baz.func_defaults[0]: p = 'default'
 ...     else:
 ...         idd = id(d)
 ...         p = baz.c.get(idd,None)
 ...         if not p:
 ...             p = 'caller%d' % len(baz.c)
 ...             baz.c[idd] = p
 ...     baz.n = baz.__dict__.get('n',0)+1
 ...     d['%s_%s'%(p,baz.n)] = baz.n
 ...     return d
 ...
 >>> baz()
 {'default_1': 1}
 >>> baz()
 {'default_2': 2, 'default_1': 1}
 >>> my_d={}
 >>> baz(my_d)
 {'caller0_3': 3}
 >>> my_d2={}
 >>> baz(my_d2)
 {'caller1_4': 4}
 >>> baz(my_d2)
 {'caller1_4': 4, 'caller1_5': 5}
 >>> baz(my_d)
 {'caller0_6': 6, 'caller0_3': 3}
 >>> baz()
 {'default_7': 7, 'default_2': 2, 'default_1': 1} 

Regards,
Bengt Richter



More information about the Python-list mailing list