Using both keyword=something and *unknownqty arguments in a function

Francis Avila francisgavila at yahoo.com
Tue Dec 23 00:43:34 CET 2003


EP wrote in message ...
>Is it possible?
>
>def skeptic(first='Zeus', second='Guido', *others)
> print first
> print second
> print others
> return "and I did not think it could ever work!"
>
>answer=skeptic('believer', 'non-believer', 'heretic', first='Eric',
>second='unknown')
>
>Asking this seriously, please forgive any cuteness.  Is there a way this
>can be made to work, or are these two types of arguments mutually
exclusive?

This particular case seems to be theoretically possible but the
implementation can't handle it.  Enter a feature request.  You may end up
having to write a PEP if this is deemed a change of semantics, and it may
very well be.

I think there's a deeper issue here: you want Python to assign default args
first, then fill in positional arguments, skipping over those already
assigned.  Python wants all arguments to be assigned positionally, and then
to have keyword arguments assigned at the end (with the result that in
certain combinations of calls and default arguments, things get assigned
twice; hence your error.)

And Python wants this for a very good reason: it's a far clearer and simpler
specification.  If you assign keyword arguments first, then positional, you
have to do all sorts of magic to figure out what the programmer really
means; and it may get it wrong, which means the programmer has to figure out
what *Python* expects, and hence has to sort through a complex spec.  In
programming languages, to strive for DWIM will always give us perl.  If DWIM
isn't right 99.9% of the time, it's far more confusing than giving a simple,
consistent spec, to which the programmer conforms.  The simple reason for
this is that people are far more adaptable than computers, and the onerous
is always on us, in the end, to learn to use a language.  So would you
rather learn a simple language, or a complex one?  A DWIM language will
inevitably be complex.

Of course, you can take this to the opposite extreme, in which we have
Forth, Lisp/Scheme, and assembly. :)

So the issue isn't the optional arguments (the '*others') at all.  For
example, should the following be allowed?

def g(a=1, b): return a,b

Should you be able to call it like this:

g(2, a=9)

and get (9, 2)?  From what you say, you're leaning towards 'yes'.  Python,
however, says explicitly, 'no', in section 7.5 of the language reference.
"If a parameter has a default value, all following parameters must also have
a default value."  I could even argue that *allowing* you to have a tuple
argument after keyword arguments is a violation of the specs.  As usual, the
specs aren't the clearest or most unambiguous, and we need to go by the
reference implementation.

IMO, I think what you propose is a bit confusing.  The function must assume
that if default arguments are specified explicitly, then they must be
ignored as positional.

Let's do a thought experiment:

def f(a=1, b=2, *c): return a,b,c

f(9) # Whose argument is this, a or c?
-> (9, 2, ()) #Python says a's.  So it interprets as positional.
f(b=9)
-> (1, 9, ())
f(a=1, 2,3,4) # Does 2 belong to b or to c?
-> SyntaxError # Python resists the urge to guess. 'import this'
f(2,3,4, a=1) # Does 2 belong to a, to b, or to c?
-> TypeError #Python says, to 'a'. And trips over itself. But true question
remains unanswered.

Here's a real toughie:

f(2,3,4,b=1) # To whom does 2, 3, and 4 belong?

You already know what Python says, but I bet you're not sure what *you*
would say, nor what some other programmer would say.  So you now see the
Python philosophy quite clearly: you may not like what Python does, but you
certainly understand it unambiguously!

Your scenario of f(3,4, a=1, b=2) is the only one where an unambiguous
answer is conceivably possible.  But if Python allows the semantics you
prefer, it will complicate the implementation and the specification, and
further it will encourage this sort of behavior, leading to the scary
possibility of a future PEP that suggests that f(2,3,4,b=1) have a legal
interpretation, too!

You should be happy enough that we have default and keyword arguments at
all: they are a tradeoff between convenience and simplicity/explicitness.  I
think Python has achieved the right balance here, and we shouldn't ask too
much, lest we regret our asking.

--
Francis Avila






More information about the Python-list mailing list