PyWart: The problem with "print"

Rick Johnson rantingrickjohnson at gmail.com
Mon Jun 3 21:37:24 EDT 2013


On Sunday, June 2, 2013 1:58:30 PM UTC-5, Steven D'Aprano wrote:
> On Sun, 02 Jun 2013 10:04:00 -0700, Rick Johnson wrote:

Oh Steven, you've really outdone yourself this time with the
theatrics. I hope you scored some "cool points" with your
minions. Heck, you almost had me convinced until i slapped
myself and realized your whole argument is just pure BS. For
the sake of the lemmings, i must dissect your BS and expose
it's methane emitting innards for all to smell.

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

And perhaps you should read a dictionary and obtain (at
minimum) a primary school level education in English before
making such foolish statements, because, OBVIOUSLY you don't
know the definition of "syntactical sugar"... shall i
educate you?


    ############################################################
    #               Wikipedia: "syntactic sugar"               #
    ############################################################
    # In computer science, syntactic sugar is syntax within a  #
    # programming language that is designed to make things     #
    # easier to read or to express. It makes the language      #
    # "sweeter" for human use: things can be expressed more    #
    # clearly, more concisely, or in an alternative style that #
    # some may prefer[...]                                     #
    ############################################################

The print function is the very definition of a "syntactic sugar".

For example:
    print("some sting")

is much more readable than:

    sys.stdout.write("some string"+"\n")

or:

    sys.stderr.write("some string"+"\n")

or:

    streamFoo.write("blah")

But wait, there's more!

    ############################################################
    #         Wikipedia: "syntactic sugar" (continued)         #
    ############################################################
    # [...]Specifically, a construct in a language is called   #
    # syntactic sugar if it can be removed from the language   #
    # without any effect on what the language can do:          #
    # functionality and expressive power will remain the same. #
    ############################################################

Again, the removal of a print function (or print statement)
will not prevent users from calling the write method on
sys.stdout or sys.stderr (or ANY "stream object" for that matter!)

The only mistake i made was to specify stdout.write
specifically instead of generally referring to the print
function as a sugar for "stream.write()".

> > 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!!!
> Only if you're a lousy programmer who doesn't understand Python's truth
> model.

I understand the Python truth model quite well, i just don't
happen to like it. Implicit conversion to Boolean breaks the
law of "least astonishment".

Many times you'll get a result (or an input) that you expect
to be a Boolean, but instead is a string. A good example of
poor coding is "dialog box return values". Take your
standard yes/no/cancel dialog, i would expect it to return
True|False|None respectively, HOWEVER, some *idiot* decided
to return the strings 'yes'|'no'|'cancel'.

If you tried to "bool test" a string (In a properly designed
language that does NOT support implicit Boolean conversion)
you would get an error if you tried this:

  py> string = " "
  py> if string:
  ...     do_something()
  ERROR: Cannot convert string to Boolean!

However, with Python's implicit conversion to Boolean, the
same conditional will ALWAYS be True: because any string
that is not the null string is True (as far as Python is
concerned). This is an example of Python devs breaking TWO
Zens at once:

 "explicit is better than implicit"
 "errors should NEVER pass silently"

And even though Python does not raise an error, it should!

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

Obviously you don't appreciate the value of "explicit enough".

  if VALUE:

is not explicit enough, however

  if bool(VALUE)

or at least:

  if VALUE == True

is "explicit enough". Whereas:

 if VALUE == True == True

is just superflous. But that's just one example. What about this:

  if lst:

I don't like that because it's too implict. What exactly
about the list are we wanting to test? Do we want to know if
we have list object or a None object, OR, do we want to know
if we have a list object AND the list has members? I prefer
to be explicit at the cost of a few keystrokes:

  if len(lst) > 0:

Covers the "has members" test and:

  if lst is not None

covers the "existence" test.

I know Python allows me to be implicit here, however, i am
choosing to be explicit for the sake of anyone who will read
my code in the future but also for myself, because being
explicit when testing for truth can catch subtle bugs.

Consider the following:

 What if the symbol `value` is expected to be a list,
 however, somehow it accidentally got reassigned to another
 type. If i choose to be implicit and use: "if value", the
 code could silently work for a type i did not intend,
 therefore the program could go on for quite some time before
 failing suddenly on attribute error, or whatever. 

However, if i choose to be explicit and use:

  "if len(VALUE) > 0:

then the code will fail when it should: at the comparison
line.  Because any object that does not provide a __len__
method would cause Python to raise NameError.

By being "explicit enough" i will inject readability and
safety into my code base. (that's twice you've been schooled
in one reply BTW!)

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

Hmm, I agree!. You're actually correct here. We should not
be reassigning constants should we? (<--rhetorical) In
correcting me you've exposed yet another design flaw with
Python. Sadly Python DOES allow reassignment of CONSTANTS.

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

Only if your language does not provide a proper debugprint
function or provide the tools to create a proper debug print
function. I detest true global variables, however, there are
some legitimate reasons for true globals in every language.
This "debugprint" problem is one of those reasons.

> > 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")

That's hideous! Two lines of code to make a single debug
message, are you joking? I realize Python will allow me to
place the entire statement on one line, however i refuse to
do that also. I am very strict about my block structure and
styles, and even the consistent inconsistency of the great
GvR will not sway me away from adherence to my consistent
readable style.


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

And perhaps you should listen to diverse ideas and be open
to change instead of clinging to your guns and religion.

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

Of course global debug overrides local debug, what's the
purpose of global switching if it cannot override local
switching? "__GLOBALDEBUG__ = False" would disables ALL
debug messages EVERYWHERE. Yes, you are correct on this
issue. It would be the same as setting a command line switch.
However, you misunderstand __LOCALDEBUG__. When global
debugging is "on" "__LOCALDEBUG__ = False" will disable
debug messages ONLY in the module for which it was declared.



More information about the Python-list mailing list