[Python-ideas] On the Python function call interface

Jonathan Fine jfine2358 at gmail.com
Tue Sep 11 06:03:02 EDT 2018


I hope that in this thread we will share and develop our understanding
of how Python handles the interface between defining a function and
calling a function.

In this message, I describe a tension between the caller and
implementer of a function. I intend in further messages to cover

    Elias Tarhini's post on __iter__(), keys(), and the mapping protocol
    [This affects the behaviour of fn(**kwargs), perhaps to our disadvantage.]
    https://mail.python.org/pipermail/python-ideas/2018-September/053320.html

    Marko Ristin-Kaufmann post on Pre-conditions and post-conditions
    [This is, in part, about wrapping a function, so it can be monitored.]
    https://mail.python.org/pipermail/python-ideas/2018-August/052781.html

and probably some other topics. My main concern is that we know what
choices are already available, and the human forces that good design
decisions will balance.


The signature of a function has at least three purposes. First, to
provide the CALLER of the function with guidance as to how the
function should be used. Second, to provide the IMPLEMENTER of the
function with already initialised variables, local to the function
body. Third, to provide both caller and implementer with visible
default values.

When there are many arguments, these two purposes can be opposed.
Here's an example. The IMPLEMENTER might want to write (not tested)

    def method(self, **kwargs):

        # Do something with kwargs.
        # Possibly mutate kwargs.
        # Change values, remove items, add items.

        # And now pass the method up.
        super().method(**kwargs)


In some case, the implementer might prefer

    def method(self, aaa, bbb, **kwargs):

        # Do something with aaa and bbb.
        super().method(**kwargs)


However, the CALLER might wish that the implementer has a signature

    def method(self, aaa, bbb, ccc, ddd, eee, fff, ggg, hhh):

and this encourages the implementer to write super().method(aaa=aaa, ...).


However, there is an alternative:

    def method(self, aaa, bbb, ccc, ddd, eee, fff, ggg, hhh):

        lcls = dict(locals())
        lcls.pop('aaa')
        lcls.pop('bbb')

        # Do something with aaa and bbb.
        super().method(**lcls)

This implementation bring benefits to both the user and the
implementer. But it's decidedly odd that the local variables ccc
through to hhh are initialised, but not explicitly used. I think it
would help to dig deeper into this, via some well-chosen examples,
taken for real code.


By the way, Steve Barnes has suggested Python be extended to provide
"access to a method, or dictionary, called __params__ which would give
access, as a dictionary, to the parameters as supplied in the call,
(or filled in by the defaults)."

[See: https://mail.python.org/pipermail/python-ideas/2018-September/053322.html]

Such a thing already exists, and I've just showed how it might be used.

    https://docs.python.org/3/library/functions.html#locals

    >>> def fn(a, b, c, d=4, e=5): return locals()
    >>> fn(1, 2, 3, e=6)
    {'e': 6, 'd': 4, 'c': 3, 'a': 1, 'b': 2}

I think, once we understand Python function and code objects better,
we can make progress without extending Python, and know better what
extensions are needed. Much of what we need to know is contained in
the inspect module:

    https://docs.python.org/3/library/inspect.html

-- 
Jonathan


More information about the Python-ideas mailing list