[Python-Dev] Replacement for print in Python 3.0 (original) (raw)

Guido van Rossum guido at python.org
Tue Sep 6 02:52:01 CEST 2005


On 9/5/05, Kay Schluehr <kay.schluehr at gmx.net> wrote:

[...] is the most heavyweight solution, but can encapsulate options and is reusable:

>>> Writer(sep="//").print("some","text") some//text or writer = Writer(sep="//", file=sys.stderr) writer.print("some","error-text") writer.print("another","error text")

I am disappointed to see several proposals plunge into this type of generality (no matter how cool it is in its application of OO design patterns) without asking whether there is a need. Look at the example -- it is completely useless. I only made it up so that I could present the simpler version; I didn't have a use case myself for arbitrary delimiters.

My hypothesis is that there are actually only two use cases that matter enough to be supported directly:

(a) quickly print a bunch of items with spaces in between them and a trailing newline

(b) print one or more items with precise control over each character

If there are other use cases they can obviously be coded by using (b) and a modest amount of custom code. (I know there's the use case of printing sequences; I'll try to give an analysis of this use case in another post if one of its proponents doesn't do so soon.)

An additional use case that I am willing to entertain because there is a lot of prior art (like Python's logging package, Bill Janssen's note(), and of course many other languages) is format-directed printing. This can of course be reduced to use case (b) using the str.% operator, but it is common enough to at least consider providing a direct solution which avoids the pitfalls of the % operator. Call this use case (c).

Interesting, use case (b) can also easily be reduced to use case (c)!

In a different thread I mentioned a design principle for which I have no catchy name, but which has often helped me design better APIs. One way to state it is to say that instead of a single "swiss-army-knife" function with various options that choose different behavior variants, it's better to have different dedicated functions for each of the major functionality types. So let's call it the "Swiss Army Knife (...Not)" API design pattern.

There are a number of reasons why this API design is often better. These aren't quite the same reasons why a real life Swiss Army knife is often inferior to individual tools, if you have them available, so the analogy isn't perfect. (So sue me. :-)

Some examples of the design pattern in action are str.strip(), str.lstrip() and str.rstrip(), or str.find() and str.rfind().

A much stronger subcase of this pattern (with fewer exceptions) is that the return type of a function shouldn't depend on the value of an argument. I have a feeling that if we were to extend the notion of type to include invariants, you'd find that the basic pattern is actually the same -- often the variant behaviors change the key invariant relationships between input and output.

OK, still with me? This, together with the observation that the only use cases for the delimiter are space and no space, suggests that we should have separate printing APIs for each of the use cases (a), (b) and (c) above, rather than trying to fold (b) into (a) using a way to parameterize the separator (and the trailing newline, to which the same argument applies). For example:

(a) print(...) (b) printraw(...) or printbare(...) (c) printf(fmt, ...)

Each can take a keyword parameter to specify a different stream than sys.stdout; but no other options are needed. The names for (a) and (c) are pretty much fixed by convention (and by the clamoring when I proposed write() :-). I'm not so sure about the best name for (b), but I think picking the right name is important.

We could decide not to provide (b) directly, since it is easily reduced to (c) using an appropriate format string ("%s" times the number of arguments). But I expect that use case (b) is pretty important, and not everyone likes having to use format strings. This could be reduced to a special case of the Swiss Army Knife (...Not) rule.

BTW we could use "from future import printing" to disable the recognition of 'print' as a keyword in a particular module -- this would provide adequate future-proofing.

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



More information about the Python-Dev mailing list