PyWart: The problem with "print"

Steven D'Aprano steve+comp.lang.python at pearwood.info
Sun Jun 2 14:58:30 EDT 2013


On Sun, 02 Jun 2013 10:04:00 -0700, Rick Johnson wrote:

> Many
> languages provide a function, method, or statement by which users can
> write easily to stdout, and Python is no exception with it's own "print"
> function. However, whilst writing to stdout via "print" is slightly less
> verbose than calling the "write" method of "sys.stdout", we don't really
> gain much from this function except a few keystrokes... is this ALL
> print should be? A mere syntactical sugar?

Perhaps you should read the docs before asking rhetorical questions, 
because the actual answer is, No, print is not mere syntactical sugar 
saving a few keystrokes.


Help on built-in function print in module builtins:

print(...)
    print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)

    Prints the values to a stream, or to sys.stdout by default.
    Optional keyword arguments:
    file:  a file-like object (stream); defaults to the current
           sys.stdout.
    sep:   string inserted between values, default a space.
    end:   string appended after the last value, default a newline.
    flush: whether to forcibly flush the stream.


Still not powerful enough for you? Easily fixed:

import builtins

# The higher the verbosity, the more messages are printed.
verbosity = 2

def print(*args, level=1, **kwargs):
    if level >= verbosity:
        builtins.print(*args, **kwargs)


print("debug message", level=4)  # Only prints if verbosity >= 4
print("info message", level=3)
print("warning message", level=2)
print("critical message", level=1)  # Only prints if verbosity >= 1


Trivial enough to embed in each module that needs it, in which case each 
module can have its own verbosity global variable. Or you can put it in a 
helper module, with a single, application-wide global variable, and use 
it like this:

import verbose_print
print = verbose_print.print
verbose_print.verbosity = 3
print("some message", level=4)

Of course, in practice you would set the verbosity according to a command 
line switch, or an environment variable, or a config file, and not hard 
code it in your source code.


> I've found that many subtle bugs are caused by not limiting the inputs
> to sane values (or types). And with Python's duct typing 

Nothing worse than having pythons roaming through your ducts, eating your 
ducks.


> and implicit
> casting to Boolean, you end up with all sorts of misleading things
> happening! Maybe you're testing for truth values and get a string
> instead; which screws everything up!!!

Only if you're a lousy programmer who doesn't understand Python's truth 
model.


> A "wise programmer" may think he's solved the problem by writing a
> function called "debugprint" that looks like this:
> 
>     def debugprint(*args):
>         if DEBUG == True:
>             print(*args)

No no no, that's not how you do it. It should be:

    if DEBUG == True == True:

Wait, no, I got that wrong. It should be:

    if DEBUG == True == True == True:

Hang on, I've nearly got it!

    if DEBUG == True == True == True == True:

Or, you could program like a professional, and say:

    if DEBUG:

By the way, why is DEBUG a constant? Doesn't that defeat the purpose?


> However that solution is at best woefully inadequate and at worse a
> cycle burner!

Certainly you don't want to be burning cycles. Imagine the pollution from 
the burning rubber tyres!


>  * Woefully inadequate because: Switching on or off the debug
>    messages is only valid in the current module that the function was
>    imported. What if you want to kill all debugprint messages
>    EVERYWHERE? 

You start by learning how Python works, and not making woefully incorrect 
assumptions.


>    Do you really want to edit "debug = BOOLEAN" in every
>    source file 

Python does not work that way.


>    OR do something stupid like import debugprint and edit
>    the DEBUG constant 

Heaven forbid that people do something that actually works the way Python 
is designed to work.


>    OR even dumber, edit the debugprint source code?
>    GAWD NO!
>
>  * But even if you are willing to cope with all the "switch-
>    on-and-off" nonsense, are you willing to have you code slowed by
>    numerous calls to a dead function containing a comparison that will
>    always be false?

And of course you have profiled your application, and determined that the 
bottleneck in performance is the calls to debugprint, because otherwise 
you are wasting your time and ours with premature optimization.

Life is hard. Sometimes you have to choose between performance and 
debugging.


> This
> realization has brought me to the conclusion that Python (and other
> languages) need a "scoped print function". What is a "scoped print
> function" anyway?  Well what i am proposing is that Python include the
> following "debug switches" in the language:
> 
>   ------------------------------
>    Switch: "__GLOBALDEBUG__"
>   ------------------------------
>   Global switching allows a programmer to instruct the interpreter to
>   IGNORE all print functions or to EVALUATE all print functions by
>   assigning a Boolean value of True or False respectively to the global
>   switch (Note: global switch always defaults to True!).

If you really care about this premature optimization, you can do this:

if __debug__:
    print("whatever")


You then globally disable these print calls by running Python with the -O 
switch.


>   Any script that includes the assignment "__GLOBALDEBUG__ = False" will
>   disable ALL debug print messages across the entire interpreter
>   namespace. In effect, all print statements will be treated as comments
>   and ignored by the interpreter. No dead functions will be called and
>   no false comparisons will be made!
> 
>   (Note: __GLOBALDEBUG__ should not be available in any local namespace
>   but accessed only form the topmost namespace. Something like:
>   __main__.__GLOBALDEBUG__ = Boolean

Python does not work like that. Perhaps you should learn how to program 
in Python before telling us how it should be improved?


>   ------------------------------
>    Switch: "__LOCALDEBUG__"
>   ------------------------------
>   Local switching allows a programmer to turn on (or off) debug messages
>   in the module it was declared. Not sure if this should be more
>   specific than modules; like classes, blocks, or functions??? Of course
>   this declaration will be overridden by any global switch.

So, it will be utterly useless then, since __LOCALDEBUG__ has no effect, 
and __GLOBALDEBUG__ overrides it. Great.


> My only
> concern is that some programmers may be confused why their print calls 
> are not working and may not have the capacity to resolve that function
> named "print" is tied to the global and local switches named
> "__GLOBAL_DEBUG__" and "__LOCAL_DEBUG__". To prevent any cognitive
> dissonance it may be desirable to introduce a new function called
> "debugprint".

That's not what cognitive dissonance means. The word you are looking for 
is "confusion".

Cognitive dissonance is the mental stress and anguish a person feels when 
deep down they know that they are the best, most intelligent, most expert 
Python programmer on the planet, better even than Python's creator, and 
yet every time they open their mouth to tell the world how Python gets it 
wrong and how to fix it, they just get shot down in flames.


-- 
Steven



More information about the Python-list mailing list