The more items that are being printed, and the complicated the format
invocation, the more valuable having it stated in-line becomes. Note that full
expressions are are supported. They are evaluated in the context of the caller.

Printing Where You Like

say() writes to a list of files–by default just sys.stdout. But
with it simple configuration call, it will write to different–even multiple–
files:

from say import say, stdout
say.setfiles(stdout, "report.txt")
say(...) # now prints to both stdout and report.txt

This has the advantage of allowing you to capture program output without changing
any code. You can also define your own targeted Say instances:

Printing When You Like

Of course, you don’t have to print to any file. There’s a predefined sayer
fmt() that works exactly like say() and inherits most of
its options, but
doesn’t print. (The
C analogy: say:fmt::printf:sprintf.)

Encodings

say() and
fmt() try to work with Unicode strings, for example providing them as
return values. But character encodings remain a fractious and often exasperating
part of IT. When writing to files, say handles this with an

If you are using Python 2.7
with strings containing utf-8 rather than Unicode characters, say will
not be greatly happy–but basically in the same places that format() is
already not happy.

When writing to files under Python 2.7, say writes using an encoding
(by default, utf-8). But you can get creative:

say('I am a truck!', encoding='base64') # SSBhbSBhIHRydWNrIQo=

Or change the default:

say.set(encoding='rot-13')

Knock yourself out with all the exciting opportunites!
If you really want the formatted text returned just as it is written to files,
use the encoded option. Set to True it returns text in the output
encoding. Or set to anything else, that becomes the return encoding.

say() returns the formatted text with one small tweak: it removes the final
newline if a newline is the very last character. Though odd, this is exactly
what you need if you’re going to print or
say the resulting text without a gratuitous “extra” newline.

A similar method hr produces just a horizontal line, like
the HTML <hr> element. For either, one can optionally
specify the width (width), character repeated to make the line (sep),
and vertical separation/whitespace above and below the item (vsep).
Good options for the separator might be be ‘-‘, ‘=’, or parts of the Unicode
box drawing character set.

Python 3

Say works the same way in both Python 2 and Python 3. This can simplify
software that should work across the versions, without all the from __future__
import hassle.

say attempts to mask some of the quirky compexities of the 2-to-3 divide,
such as string encodings and codec use.

Alternatives

ScopeFormatter is a
module that provides variable interpolation into strings. It is amazingly
compact and elegant. Sadly, it only interpolates Python names, not full
expressions. say has full expressions, as well as a framework for
higher-level printing features beyond ScopeFormatter’s…um…scope.

Unfortunately this has even more limitations than ScopeFormatter: it only supports
local variables, not globals or expressions. And the interpolation code seems
gratuitous. Simpler:

say("Hello, {name}!")

Notes

The say name was inspired by Perl’s say,
but the similarity stops there.

Automated multi-version testing with the wonderful
pytest
and tox modules has commenced. say is now
successfully packaged for, and tested against, all late-model verions of
Python: 2.6, 2.7, 3.2, and 3.3.

say has greater ambitions than just simple template printing. It’s part
of a larger rethinking of how output should be formatted. Stay tuned.

In addition to being a practical module in its own right, say is
testbed for options, a package
that provides high-flexibility option, configuration, and parameter
management.