gratuitous new features in 2.0

Guido van Rossum guido at beopen.com
Fri Aug 25 09:53:40 EDT 2000


Hey folks, I am aware of this thread.  Think of this reply as a lesson
in Pythonic thinking. :-)

[Robin Becker]
> here here, I hate the >> construct,
> why not 
> 
>         print in stderr items,.... 
>         print + stderr items,.......
>         print[stderr] items,.....
>         print to stderr items,.....

In the grand scheme of the objections to this feature these are nits,
but since you bring them up first, let me answer them first.  There
are a number of issues.

If we want to use a special symbol (print <symbol> expression), the
Python parser requires that it is not already a symbol that can start
an expression -- otherwise it can't decide which form of print
statement is used.  (The Python parser is a simple LL(1) or recursive
descent parser.)

This means that we can't use the "keyword only in context trick" that
was used for "import as", because an identifier can start an
expression.  This rules out +stderr, [sterr], and to stderr.  It
leaves us with binary operator symbols and other miscellaneous symbols
that are currently illegal here, such as 'import'.

If I had to choose between 'print in file' and 'print >> file' I would
definitely choose '>>'.  In part because 'in' would be a new invention
(I know of no other language that uses it, while '>>' is used in sh,
awk, Perl, and C++), in part because '>>', being non-alphabetic,
stands out more so is more likely to catch the reader's attention.

The comma separating the file from the following expression is
necessary!  Of course you want the file to be an arbitrary expression,
not just a single word.  (You definitely want to be able to write
print >>sys.stderr.)  Without the expression the parser would't be
able to distinguish where that expression ends and where the next one
begins, e.g.

  print >>i +1, 2
  print >>a [1], 2
  print >>f (1), 2

All the above is just in defense of the details of the current syntax
given that we want to add an option to the print statement rather than
using a function; I'll defend that next.

> I would prefer a writeln(stderr,item,.....), but then I can already do
> that myself.

If you state it this way, a method would be more appropriate; we could
have added a method writeln(item, ...) to standard file objects.

First of all, this is lacking a feature of the print statement: the
trailing comma to print which suppresses the final newline.  Note that
'print a,' still isn't equivalent to 'sys.stdout.write(a)' -- print
inserts a space between items, and takes arbitrary objects as
arguments; write() doesn't insert a space and requires a single
string.

When you are considering an extension for the print statement, it's
not right to add a function or method that adds a new feature in one
dimension (where the output goes) but takes away in another dimension
(spaces between items, and the choice of trailing newline or not).  We
could add a whole slew of methods or functions to deal with the
various cases but that seems to add more confusion than necessary, and
would only make sense if we were to deprecate the print statement
altogether.

I feel that this debate is really about whether print should have been
a function or method rather than a statement.  If you are in the
function camp, of course adding special syntax to the existing print
statement is not something you like.  I suspect the objection to the
new syntax comes mostly from people who already think that the print
statement was a bad idea.  Am I right?

About 10 years ago I debated with myself whether to make the most
basic from of output a function or a statement; basically I was trying
to decide between "print(item, ...)" and "print item, ...".  I chose
to make it a statement because printing needs to be taught very early
on, and is very important in the programs that beginners write.  Also,
because ABC, which lead the way for so many things, made it a
statement.  In a move that's typical for the interaction between ABC
and Python, I changed the name from WRITE to print, and reversed the
convention for adding newlines from requiring extra syntax to add a
newline (ABC used trailing slashes to indicate newlines) to requiring
extra syntax (the trailing comma) to suppress the newline.  I kept the
feature that items are separated by whitespace on output.

Full example: in ABC,
    WRITE 1
    WRITE 2/
has the same effect as
    print 1,
    print 2
has in Python, outputting in effect "1 2\n".

I'm not 100% sure that the choice for a statement was right (ABC had
the compelling reason that it used statement syntax for anything with
side effects, but Python doesn't have this convention), but I'm also
not convinced that it's wrong.  I certainly like the economy of the
print statement.  (I'm a rabid Lisp-hater -- syntax-wise, not
semantics-wise! -- and excessive parentheses in syntax annoy me.
Don't ever write return(i) or if(x==y): in your Python code! :-)

Anyway, I'm not ready to deprecate the print statement, and over the
years we've had many requests for an option to specify the file (but
that's all in the PEP).

>From a post by Ralph Corderoy:

> This seems to be the start of the slippery slope to Perl where there
> are many, many, ways to do it and you have to learn that >> means
> right-shift, *except*...

While I'm not in favor of making things context dependent, I don't
believe that this argument holds.  There are lots of symbols in Python
that have multiple meanings.  How many meanings for a comma do you
know?  For a colon?  For square brackets?  For parentheses?  For the
'in' keyword?  For the '%' operator?  None of these seem to bother
people much.

[Alex Martelli]
> I haven't seen one either, including the URL you kindly give for
> Tim Peters' comments.  Apparently, the 'print>>bah' syntax is
> only being compared with the current situation, rather than with
> the many alternative ways of exposing the same functionality:
> 
>     print >> bah, foo, bar, baz
>     println(bah, foo, bar, baz)
>     println(foo, bar, baz, file=bah)
>     sys.println as above
>     bah.println(foo, bar, baz)
> 
> I absolutely can't see why the first syntax is deemed to be "more
> pythonic" than the others;

The first syntax is more Pythonic because it's an extension of an
existing Python feature.  The typical sequence of explanation to a
newbie would go as follows:

  To print data, use "print item, ...".  Oh, it goes to a file named
  sys.stdout.  If you want it to go elsewhere, e.g. to sys.stderr, use
  this extension: print >>sys.stdout, item, ...".

With any of the function alternatives, this would become:

  To print data, use "print item, ...".  Oh, it goes to a file named
  sys.stdout.  If you want it to go elsewhere, e.g. to sys.stderr, use
  the following completemy different syntax:
  "sys.stdout.writeln(item, ...)".

> How do you implement 'print
> this info to this set of files' if print is a statement, not
> a function?  You presumably have to implement a tee-object,
> say:
> class teeObject:
>     def __init__(self, *files):
>         self.files=files
>     def write(self, stuff):
>         for file in self.files:
>             write(file, stuff)
> and do something like:
>     print >> teeObject(afile, another), foo, bar, baz
> as opposed to wrapping it easily in a function:
>     teePrint((afile,another), foo, bar, baz)
> with
> def teePrint(files, *stuff):
>     for file in files:
>         println(file, *stuff)

I find the first solution (teeObject class) better than the second
(teePrint function), regardless of whether print is a function or a
statement.  The teeObject solution can be made to work in a way that
the code that does the printing isn't even aware of its existence.
This is important because often you find that you are using an
existing module that uses print!  With teeObject you can use the old
"assign to sys.stdout and use a try/finally to restore safely" trick.
With teePrint you'd have to edit the existing module to take an
optional file argument and change all its print statements to write
statements.  That's bad.

> The class-based solution, made necessary by the statement,
> appears to be about twice as complicated as the function
> based solution which a println function would enable.

Because it is so much more general and powerful!  The teePrint
solution can easily be adapted to work with the extended print
statement:

  def teePrint(files, *stuff):
      for file in files:
          for item in *stuff:
              print item,
          print

> If I just wanted to output, for debugging purposes, a
> bunch of stuff which I have received as arguments, the
> function shines again, I think:
> 
> def trace1(header,*stuff,file=sys.stdout):
>     println(file,header,*stuff)

Alas, this syntax doesn't work.  You can't have a named keyword
argument after *stuff.  If you need the file keyword argument, you'd
have to do:

  def trace1(header, *stuff, **kwds):
      file = kwds.get("file", sys.stdout)
      ...

> def myfunc(*stuff):
>     trace1("myfunc args:",*stuff):
>     # now do the real work
> 
> versus
> 
> def trace2(header,*stuff,file=sys.stdout):
>     print >> file, header,
>     for stiff in stuff[:-1]:
>         print >> file, stiff,
>     if len(stuff)>0:
>         print >> file, stuff[-1]

Try this:

  def trace2(header,*stuff,**kwds):
      file = kwds.get("file", sys.stdout)
      print >> file, header,
      for stiff in stuff:
          print >> file, stiff,
      print >> file

> Again, having to work with a print statement rather than
> a println function does seem to me to make things much,
> much more complicated -- several times as much.

In reality (where your typical trace function does a few more things,
like returning early if no debug output is requested)
I don't believe it's several times more complicated -- only two lines
more complicated.

[anonymous quoted by Alex]
> > Note that while I don't have a strong visceral reaction against
> > dict.setdefault(), I have to agree with the dismay at the speed with
> > which it's going in.

It was debated extensively on python-dev: start at
http://www.python.org/pipermail/python-dev/2000-August/014531.html
and follow the thread from there.

Maybe we should invite you to the python-dev list.  As the wxPython
developer you deserve to be included!  (So we can brainwash you to
like the next unpythonic change we're planning before it's
released. :-)

[Robin Becker, in a later post]
> As Tim Peters seems to like having the generality of an argument to the
> print statement why can't we discuss this? Can I do 
> 
>         print >> dingo, items....
> 
> where dingo has a write method (perhaps a StringIO instance)? 

Of course!

> How about print >> (dingo, dongo, dango), items.....

No -- (dingo, dongo, dango) doesn't have a write() method.

> Why do we need the first comma?

Explained above.

> Why no discussion about which operator
> was to be overloaded. It seems to me that
> 
>         print 1>>x, thing
>         print  >>x, thing
> are now very close typing wise, but semantically miles apart.

You're being silly here.  Explained above.

> I personally would prefer
> 
>         print to filetypething items,......
> 
> but no vote is to be taken on this. Perhaps the glorious leadership is
> becoming dicatorial.

It was proposed, considered, and rejected on the grounds explained
above (the syntax would be ambiguous).

--Guido van Rossum (home page: http://www.pythonlabs.com/~guido/)



More information about the Python-list mailing list