[Python-Dev] PEP 3102: Keyword-only arguments

Zachary Pincus zpincus at stanford.edu
Sun Apr 30 19:40:19 CEST 2006


Some thoughts from a lurker, largely concerning syntax; discount as  
you wish.

First:
>      Keyword-only arguments are not required to have a default value.
>      Since Python requires that all arguments be bound to a value,
>      and since the only way to bind a value to a keyword-only argument
>      is via keyword, such arguments are therefore 'required keyword'
>      arguments.  Such arguments must be supplied by the caller, and
>      they must be supplied via keyword.

So what would this look like?
   def foo(*args, kw1, kw2):

That seems a bit odd, as my natural expectation wouldn't be to see  
kw1 ands kw2 as required, no-default keyword args, but as misplaced  
positional args.

Perhaps this might be a little better?
   def foo(*args, kw1=, kw2=):
I'm rather not sure. At least it makes it clear that kw1 and kw2 are  
keyword arguments, and that they have no default values.

Though, I'm kind of neutral on the whole bit -- in my mind "keyword  
args" and "default-value args" are pretty conflated and I can't think  
of any compelling reasons why they shouldn't be. It's visually handy  
when looking at some code to see keywords and be able to say "ok,  
those are the optional args for changing the handling of the main  
args". I'm not sure where the big win with required keyword args is.

For that matter, why not "default positional args"? I think everyone  
will agree that seems a bit odd, but it isn't too much odder than  
"required keyword args". (Not that I'm for the former! I'm just  
pointing out that if the latter is OK, there's no huge reason why the  
former wouldn't be, and that is in my mind a flaw.)

Second:
>   def compare(a, b, *, key=None):
This syntax seems a bit odd to me, as well. I always "understood" the  
*args syntax by analogy to globbing -- the asterisk means to "take  
all the rest", in some sense in both a shell glob and *args.

In this syntax, the asterisk, though given a position in the comma- 
separated list, doesn't mean "take the rest and put it in this  
position." It means "stop taking things before this position", which  
is a bit odd, in terms of items *in* an argument list.

I grant that it makes sense as a derivation from "*ignore"-type  
solutions, but as a standalone syntax it feels off. How about  
something like:
   def compare(a, b; key=None):

The semicolon is sort of like a comma but more forceful; it ends a  
phrase. This seems like a logical (and easily comprehended by  
analogy) use here -- ending the "positional arguments" phrase.

Zach Pincus

Program in Biomedical Informatics and Department of Biochemistry
Stanford University School of Medicine


On Apr 29, 2006, at 11:27 AM, Talin wrote:

> PEP: 3102
> Title: Keyword-Only Arguments
> Version: $Revision$
> Last-Modified: $Date$
> Author: Talin <talin at acm.org>
> Status: Draft
> Type: Standards
> Content-Type: text/plain
> Created: 22-Apr-2006
> Python-Version: 3.0
> Post-History:
>
>
> Abstract
>
>      This PEP proposes a change to the way that function arguments are
>      assigned to named parameter slots.  In particular, it enables the
>      declaration of "keyword-only" arguments: arguments that can only
>      be supplied by keyword and which will never be automatically
>      filled in by a positional argument.
>
>
> Rationale
>
>      The current Python function-calling paradigm allows arguments to
>      be specified either by position or by keyword.  An argument  
> can be
>      filled in either explicitly by name, or implicitly by position.
>
>      There are often cases where it is desirable for a function to  
> take
>      a variable number of arguments.  The Python language supports  
> this
>      using the 'varargs' syntax ('*name'), which specifies that any
>      'left over' arguments be passed into the varargs parameter as a
>      tuple.
>
>      One limitation on this is that currently, all of the regular
>      argument slots must be filled before the vararg slot can be.
>
>      This is not always desirable.  One can easily envision a function
>      which takes a variable number of arguments, but also takes one
>      or more 'options' in the form of keyword arguments.  Currently,
>      the only way to do this is to define both a varargs argument,
>      and a 'keywords' argument (**kwargs), and then manually extract
>      the desired keywords from the dictionary.
>
>
> Specification
>
>      Syntactically, the proposed changes are fairly simple.  The first
>      change is to allow regular arguments to appear after a varargs
>      argument:
>
>          def sortwords(*wordlist, case_sensitive=False):
>             ...
>
>      This function accepts any number of positional arguments, and it
>      also accepts a keyword option called 'case_sensitive'.  This
>      option will never be filled in by a positional argument, but
>      must be explicitly specified by name.
>
>      Keyword-only arguments are not required to have a default value.
>      Since Python requires that all arguments be bound to a value,
>      and since the only way to bind a value to a keyword-only argument
>      is via keyword, such arguments are therefore 'required keyword'
>      arguments.  Such arguments must be supplied by the caller, and
>      they must be supplied via keyword.
>
>      The second syntactical change is to allow the argument name to
>      be omitted for a varargs argument:
>
>          def compare(a, b, *, key=None):
>              ...
>
>      The reasoning behind this change is as follows.  Imagine for a
>      moment a function which takes several positional arguments, as
>      well as a keyword argument:
>
>          def compare(a, b, key=None):
>              ...
>
>      Now, suppose you wanted to have 'key' be a keyword-only argument.
>      Under the above syntax, you could accomplish this by adding a
>      varargs argument immediately before the keyword argument:
>
>          def compare(a, b, *ignore, key=None):
>              ...
>
>      Unfortunately, the 'ignore' argument will also suck up any
>      erroneous positional arguments that may have been supplied by the
>      caller.  Given that we'd prefer any unwanted arguments to  
> raise an
>      error, we could do this:
>
>          def compare(a, b, *ignore, key=None):
>              if ignore:  # If ignore is not empty
>                  raise TypeError
>
>      As a convenient shortcut, we can simply omit the 'ignore' name,
>      meaning 'don't allow any positional arguments beyond this point'.
>
>
> Function Calling Behavior
>
>      The previous section describes the difference between the old
>      behavior and the new.  However, it is also useful to have a
>      description of the new behavior that stands by itself, without
>      reference to the previous model.  So this next section will
>      attempt to provide such a description.
>
>      When a function is called, the input arguments are assigned to
>      formal parameters as follows:
>
>        - For each formal parameter, there is a slot which will be used
>          to contain the value of the argument assigned to that
>          parameter.
>
>        - Slots which have had values assigned to them are marked as
>          'filled'.  Slots which have no value assigned to them yet are
>          considered 'empty'.
>
>        - Initially, all slots are marked as empty.
>
>        - Positional arguments are assigned first, followed by keyword
>          arguments.
>
>        - For each positional argument:
>
>           o Attempt to bind the argument to the first unfilled
>             parameter slot.  If the slot is not a vararg slot, then
>             mark the slot as 'filled'.
>
>           o If the next unfilled slot is a vararg slot, and it does
>             not have a name, then it is an error.
>
>           o Otherwise, if the next unfilled slot is a vararg slot then
>             all remaining non-keyword arguments are placed into the
>             vararg slot.
>
>        - For each keyword argument:
>
>           o If there is a parameter with the same name as the keyword,
>             then the argument value is assigned to that parameter  
> slot.
>             However, if the parameter slot is already filled, then  
> that
>             is an error.
>
>           o Otherwise, if there is a 'keyword dictionary' argument,
>             the argument is added to the dictionary using the keyword
>             name as the dictionary key, unless there is already an
>             entry with that key, in which case it is an error.
>
>           o Otherwise, if there is no keyword dictionary, and no
>             matching named parameter, then it is an error.
>
>        - Finally:
>
>           o If the vararg slot is not yet filled, assign an empty  
> tuple
>             as its value.
>
>           o For each remaining empty slot: if there is a default value
>             for that slot, then fill the slot with the default value.
>             If there is no default value, then it is an error.
>
>      In accordance with the current Python implementation, any errors
>      encountered will be signaled by raising TypeError.  (If you want
>      something different, that's a subject for a different PEP.)
>
>
> Backwards Compatibility
>
>      The function calling behavior specified in this PEP is a superset
>      of the existing behavior - that is, it is expected that any
>      existing programs will continue to work.
>
>
> Copyright
>
>      This document has been placed in the public domain.
>
> Local Variables:
> mode: indented-text
> indent-tabs-mode: nil
> sentence-end-double-space: t
> fill-column: 70
> coding: utf-8
> End:
> _______________________________________________
> Python-Dev mailing list
> Python-Dev at python.org
> http://mail.python.org/mailman/listinfo/python-dev
> Unsubscribe: http://mail.python.org/mailman/options/python-dev/ 
> zpincus%40stanford.edu



More information about the Python-Dev mailing list