[Python-3000] Type annotations: annotating generators

Guido van Rossum guido at python.org
Sun May 21 23:25:02 CEST 2006


On 5/21/06, Collin Winter <collinw at gmail.com> wrote:
> On 5/21/06, Aahz <aahz at pythoncraft.com> wrote:
> > On Sun, May 21, 2006, Collin Winter wrote:
> > >
> > > This is something I've been meaning to bring up: if we're serious
> > > about the "tuples = fixed-length collections, lists = unknown-length
> > > collections" distinction, then the CPython core should switch over to
> > > this mindset as well. The fact that Python 2.x uses a tuple for
> > > *varargs has caused me to jump through a number of hoops in my own
> > > typecheck package.
> >
> > This is a guideline, not a rule, and the speed and size of tuples make
> > them more appropriate for varargs.
>
> Honest question: what is so special about *varargs that demands
> tuple's speed and size characteristics?

I'm not sure -- it might depend on the application.

But I can shed some light on this like no-one else... :-)

It's mostly a historical accident. A very early version of Python
didn't have *varargs, but had a different feature: f(a, b, c) was
equivalent to f((a, b, c)). Also, def f(a, b, c) was equivalent to
today's def f((a, b, c)). This was all borrowed from ABC (which went
one step further -- f a was equivalent to f(a); but I didn't like that
enough to copy it). The idea was that a function really had only one
argument, and that multiple arguments were simulated by passing a
tuple -- just like what we still do for return values. A varargs
function was then declared with a single argument and called with
multiple ones. For example, this was a common idiom for a varargs
method:

  def foo(args):
      self, args = args[0], args[1:]
      ...

It was a bit trickier to write code that would also handle the case
where there were zero args -- because then the single argument wasn't
turned into a tuple!

The realization that this was a common use case made me change it all
around, to the current model. (I think keyword arguments and default
values came a bit later.)

The conclusion to draw from this history lesson is that the
interpretation of an argument list as a tuple was logical in the
original version, since syntactically it *was* a tuple (except in
f(a), where it *wasn't* -- just like (a, b) is a tuple but (a) isn't).
Tuples ended up significantly in the implementation as a result of the
early version, and the rewrite didn't remove them, since all C code
was also expecting tuples and I didn't want to have to change that --
except there was also C code that expected that a *single* argument
was *not* passed as a tuple, and for the longest times there were
hacks to work around that expectation: track the history of
PyArg_Parse and friends.

Nowadays the implementation contains a variety of hacks to *avoid*
creating tuples during a call -- since the arguments are present on
the caller's stack in the right order, it's often easier to present
the callee (at the C level) with a count and a pointer to an array of
objects. But since *args historically returned a tuple, if it is
present, a tuple is created.

Since **kwds is already a dict (i.e. a mutable container) and since we
long accept a list for *vargs on the call side, I think it is actually
more logical to use a list for receiving *args.

-- 
--Guido van Rossum (home page: http://www.python.org/~guido/)


More information about the Python-3000 mailing list