[Python-3000] Minor hitch writing the Function Signature PEP

Talin talin at acm.org
Sun Apr 23 21:04:54 CEST 2006


Bill Birch <birchb <at> tpg.com.au> writes:

> 
> On Sun, 23 Apr 2006 05:36 am, Talin wrote:
> >  <at> precondition( y=NotNegative )
> > def power( x, y ):
> >    ...
> > ...where 'NotNegative' is a function which simply does an assert( value >= 
> 0 )
> The phrase "y=NotNegative" is a classic type constraint predicate.
> 
> Given that type expressions will be dynamic (see GVD blog 
> http://www.artima.com/weblogs/viewpost.jsp?thread=87182)  
> how about:
> 
> def power(x, y : NotNegative):
>    body...
> 
> or
> 
> def power(x, y : not Negative):
>    body...
> 
> or even:
> 
> def power(x, y : (lambda t: t > 0) ):
>    body...
> 


That's cool, but it still doesn't solve my problem :)

Let me see if I can explain this better. Think of a function as a box. Inside
the box, there's code that expects an array of filled parameter slots. Outside
the box are decorators, classes, and all the rest of the program.

The inputs to the box are always:

   function( PyTuple *args, PyDict *kwargs )

However, inside the box, the values within 'args' and 'kwargs' get assigned to
formal parameter slots, based on the function signature.

Now, one peculiarity of this mapping procedure is that it has to be done for
all arguments, you can't just do it for a single argument in isolation. You
have no idea where any positional argument is going to go until all keyword
arguments have been placed, and until all previous positional arguments have
been placed.

Now, the __signature__ attribute gives a description of what the code inside the
box expects. However, that's not what we have to work with. We can't access the
code inside the box, we can only feed in values through the parameter mapping
mechanism. Which means that any values we want to send to the code have to
be represented as '*args, *kwargs'.

Now, if we're writing a decorator where we already know what the signature is,
its fairly easy to reverse the mapping process in our heads and write code such
that the proper values will fall into the proper places. I know that if a
function takes '(a, b, c, d)', and I write '(d=1, 2, 3, 4)' I know that the
value assignments will be 'a=2, b=3, c=4, d=1'. However in this case there's
no need for a __signature__ since I already know what the signature is.

But a generic wrapper function has to look at the __signature__ attribue to
find this out. But it can't use the signature directly, because the signature
only tells it what is going on inside the box, not what to feed the box from the 
outside. In order to do this, it has to construct a reverse mapping, and it
can't do this for just one parameter, it has to do it for all of them.

Moreover, the reverse mapping can't be constructed statically, because it
changes depending on the values contained in *args, *kwargs.

Its not only decorators that have this problem - its *any* entity that attempts
to use the __signature__ attribute to examine specific parameter values.
(Obviously, code that uses __signature__ for documentation purposes has no
problem.)

At this point, the best solution seems to be to have some standard library
function that duplicates the mapping process - that is, given a args, kwargs,
and signature object, it returns a tuple with the values in the proper parameter
slots. However, this means that you have to do the whole mapping twice, and it
still doesn't let you modify the input parameters to the function.

-- Talin




More information about the Python-3000 mailing list