[Python-ideas] Keyword only argument on function call

Steven D'Aprano steve at pearwood.info
Sat Sep 8 05:04:06 EDT 2018


On Fri, Sep 07, 2018 at 06:59:45AM -0700, Anders Hovmöller wrote:

> Personally I think readability suffers greatly already at two arguments if 
> none of the parameters are named.

*At* two arguments? As in this example?

    map(len, sequence)


I'll admit that I struggle to remember the calling order of list.insert, 
I never know which of these I ought to write:

    mylist.insert(0, 1)
    mylist.insert(1, 0)

but *in general* I don't think two positional arguments is confusing.


> Sometimes you can sort of fix the 
> readability with function names like do_something_with_a_foo_and_bar(foo, 
> bar), but that is usually more ugly than just using keyword arguments.

It is difficult to judge the merit of that made-up example. Real 
examples are much more convincing and informative.

> Functions in real code have > 2 arguments.

Functions in real code also have <= 2 arguments.


> Often when reading the code the 
> only way to know what those arguments are is by reading the names of the 
> parameters on the way in, because it's positional arguments.

I don't understand that sentence. If taken literally, the way to tell 
what the arguments are is to look at the arguments.

I think you might mean the only way to tell the mapping from arguments 
supplied by the caller to the parameters expected by the called function 
is to look at the called function's signature.

If so, then yes, I agree. But why is this relevent? You don't have to 
convince us that for large, complex signatures (a hint that you may
have excessively complex, highly coupled code!) keyword arguments are 
preferable to opaque positional arguments. That debate was won long ago. 
If a complex calling signature is unavoidable, keyword args are nicer.

> But those aren't checked.

I don't understand this either. Excess positional arguments aren't 
silently dropped, and missing ones are an error.


> To me it's similar to bracing for indent: you're telling 
> the human one thing and the machine something else and no one is checking 
> that those two are in sync. 

No, you're telling the reader and the machine the same thing.

func(a, b, c)

tells both that the first parameter is given the argument a, the second 
is given argument b, and the third is given argument c.

What's not checked is the *intention* of the writer, because it can't 
be. Neither the machine nor the reader has any insight into what I meant 
when I wrote the code (not even if I am the reader, six weeks after I 
wrote the code).

Keywords help a bit with that... it's harder to screw up 

    open(filename, 'r', buffering=-1, encoding='utf-8', errors='strict')

than:

    open(filename, 'r', -1, 'utf-8', 'strict')

but not impossible. But again, this proposal isn't for keyword 
arguments. You don't need to convince us that keyword arguments are 
good.


> I have seen beginners try:
> 
> def foo(b, a):
>     pass
> 
> a = 1
> b = 2
> foo(a, b)
> 
> and then be confused because a and b are flipped. 

How would they know?

Beginners are confused by many things. Coming from a background in 
Pascal, which has no keyword arguments, it took me a while to get to 
grips with keyword arguments:

def spam(a, b):
    print("a is", a)
    print("b is", b)

a = 1
b = 2
spam(a=b, b=a)
print(a, b)


The effect of this, and the difference between the global a, b and local 
a, b, is not intuitively obvious.


-- 
Steve


More information about the Python-ideas mailing list