[Python-Dev] Python Library Addition: First-class Procedure Signatures

Brett Cannon brett at python.org
Thu Nov 15 21:18:41 CET 2007


On Nov 15, 2007 8:42 AM, Isaac Morland <ijmorlan at cs.uwaterloo.ca> wrote:
> On Wed, 14 Nov 2007, Brett Cannon wrote:
>
> > As Collin already pointed out, it sounds like you want PEP 362 to get
> > into the stdlib.  I have not made a big push to try to get my existing
> > implementation into Python 2.6/3.0, but I plan to at some point.
>
> Yes, it had not occurred to me to check the existing PEPs for my PEP
> before proposing it.  Now that I've read it I have a couple of comments,
> but it is close to what I'm looking for.
>
> >> I've put the code below, but I wonder if the real solution is just to create an
> >> interface to already-existing capability?  It occurs to me that the
> >> implementation is likely to be in the interpreter itself and not written in
> >> Python.
> >
> > I don't see why a Python implementation is bad.  If you make this
> > information lazy then it is not such a big deal to have it take a
> > little bit longer than if it was implemented in C.
>
> I don't have any problem with a Python implementation.  In particular, I'm
> not concerned in this case about the performance.  Rather what I actually
> wanted was a way to just bind arguments and then get the resulting
> dictionary (what would usually become locals()).  I realized that I could
> create a Signature object with a binding method, but that I would just be
> duplicating part of the Python interpreter.
>
> I haven't studied the Python interpreter so I don't know if it is feasible
> to re-use that (presumably highly optimized for actually calling
> procedures, not just binding arguments) code or if it makes more sense to
> simply re-implement it.
>
> >> One possible improvement (and I'm not sure it's better, so I'm just putting it
> >> out there): perhaps expand_args should be renamed to __call__. Then essentially
> >> a Signature object would be a procedure whose body is just "return locals ()".
> >
> > __call__ is already used a method name for objects that can be called.
>
> Yes, that is why I used that name.  The idea is that a Signature object be
> callable, have itself as signature, and return the dictionary of locals
> resulting from the procedure call argument binding process.
>
> You can also think of (my idea of) Signature objects as providing a simple
> way to create lots of special-case dictionary constructors.
>
> More (semi-)formally, if the body of a procedure p is "return locals ()",
> then p(...) is the same as p.__signature__(...).
>

Fair enough, but I prefer having a method for it.

> A couple of comments about PEP-362:
>
> 1. For the "name" attribute of the Parameter object, I think it needs to
> be str | tuple(str) | tuple(tuple(str)) | ....:
>
> >>> def a ((b,c),(d,(e,f))):
> ...     print b,c,d,e,f
> ...
> >>> a
> <function a at 0x7899b0>
> >>> a((1,2),(3,(4,5)))
> 1 2 3 4 5
> >>>

You are taking annotations a little too far in terms of typing.  =)
There are no type checks for the annotations; they are for
documentation purposes only.

Plus tuple parameters are gone as of Py3K thanks to my prodding to
ditch them so I am really not going to worry about them now.  =)

>
> 2. For "position" of keyword-only parameters, are they allowed to conflict
> with each other, or does each parameter get a unique value?  +1 on not
> using -1 as a special value.  Python is not COBOL.
>

It is just he numeric order that the arguments are found, period.
Keyword-only arguments are given a index position, it just doesn't
really mean much other than giving it a numeric position.

> 3. (My apologies if any of these have already been discussed)  Under
> "Implementation", could __signature__ just be a property of callable
> objects?  Not saying anything about implementation, but just being able to
> say "formataddr.__signature__" feels nicely minimal (to me).

The idea has been to add the code to the stdlib and let people try
them out first.  If they ended up being used often enough then
discussion of putting a signature object on every callable could be
discussed.

>
> 4. Signature.bind - for what I'm doing, I definitely want what would
> become locals() in a procedure call, i.e. keys in the resulting dictionary
> are strings.  But I can see the other behaviour being useful in other
> circumstances so maybe there should be bind and bindp, or (see above)
> __call__ and bind, or something else.
>

I only want to bother supporting one or the other.  Doing the reverse
is one or two lines of code.

> 5. var_args... default to None.  +10 from me on this one - this is
> *exactly* what None is, as far as I can tell.  I'm new enough that this
> should probably count for at most +0.1 though.
>
> 6. The PEP doesn't say anything about building Signature objects from
> scratch, and the code does not reassure me.

That's because there is no way.  =)

>  I would like to be able to
> build a Signature from a sequence of strings for positional parameter
> names, for example, and provide default values for some parameters.  My
> solution started off:
>
> class Signature (object):
>       def __init__ (self, argnames,
>        excessargs=None, excesskeys=None, defaults=None):
>           self.__argnames = tuple (argnames)
>           self.__excessargs = excessargs
>           self.__excesskeys = excesskeys
>           if defaults is None:
>               defaults = {}
>           self.__defaults = dict (defaults)
> [....]

I really don't see this being used often enough to warrant the need to
support building a Signature object from scratch instead of an actual
callable.  It's easy enough to create a string for a function that has
what you want, compile it, and get the Signature object from that.

-Brett


More information about the Python-Dev mailing list