PyWart: The problem with "print"

Rick Johnson rantingrickjohnson at gmail.com
Sun Jun 2 13:04:00 EDT 2013


Note to those of you who may be new to Python: I will refer to "print" as a function -- just be aware that "print" was a statement before Python3000 was introduced.

------------------------------------------------------------
 Introduction:
------------------------------------------------------------
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? For me, i think the "print" function CAN and SHOULD be so much more!

------------------------------------------------------------
 Print's Role in Debugging:
------------------------------------------------------------
A print function can be helpful in many ways, and one very important role print plays is to inform the programmer of internal states of objects via debug messages written to stdout.

Sure, there are fancy debuggers by which internal state and object identity can be inspected on the fly, however, "print" is always going to be there no matter what libraries or add-on you have available. And let's face it folks, print is the most simplistic and intuitive interface you'll ever find for debugging purposes. Sure, "print" may not be the most productive for large scale debugging, but for the majority of debugging tasks, it's a good fit.

I know some of you will cringe at the idea of using print for debugging, however, i will argue that using a debugger can weaken your detective skills. If an exception message and trackback are not giving you enough information to find the bug, well then, the language OR the code you've written is not worth a monkey's toss! 

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 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!!! 

Anyhoo, i digress...

------------------------------------------------------------
 Inadequacies of "print" Debugging.
------------------------------------------------------------
In it's current implementation, print is helpful, but in many ways, print is lacking it's true potential. Many of the problems that propagate up when using print as a debugger focus on the fact that you cannot easily switch the debug messages "on" or "off".

Sure, you can comment-out all calls to print, but if you need to see the messages again you will be forced to uncomment all the lines again... hey that's no fun! 

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)

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

 * 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? Do you really want to edit
   "debug = BOOLEAN" in every source file OR do something
   stupid like import debugprint and edit the DEBUG constant
   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?

    ## START INTERACTIVE SESSION ##
    py> from __future__ import print_function
    py> DEBUG = True
    py> def debugprint(*args):
    ...     if not DEBUG:
    ...         return
    ...     print(*args)
    py> debugprint("foo", "bar")
    foo bar
    py> DEBUG = False
    py> code = compile('debugprint("foo", "bar")', '<string>', 'exec')
    py> import dis
    py> dis.disassemble(code)
      1           0 LOAD_NAME                0 (debugprint)
                  3 LOAD_CONST               0 ('foo')
                  6 LOAD_CONST               1 ('bar')
                  9 CALL_FUNCTION            2
                 12 POP_TOP
                 13 LOAD_CONST               2 (None)
                 16 RETURN_VALUE
   ## END INTERACTIVE SESSION ##
   After a few million executions of this superfluous
   comparison your cpu is losing faith in your ability to
   write logical code!

   py> function.call() + false_comparison() == 'cycle burner'
   "YOU BET YOU A$$ IT DOES!!"

------------------------------------------------------------
 Solution.
------------------------------------------------------------
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!).

  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

  ------------------------------
   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.

------------------------------------------------------------
 Concerns:
------------------------------------------------------------
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".

*school-bell-rings*



More information about the Python-list mailing list