print is not a function

Alex Martelli aleax at aleax.it
Wed Oct 8 08:27:06 EDT 2003


Karl Scalet wrote:
   ...
> Thank you for the answers.

You're welcome.

> I did not realize that taking only "the half of list comprehension"
> is a misuse of it. That would mean list comprehension always "claim"
> to be assigned to a variable. But why?

Not really: any normal use of an expression is just as fine for a
list comprehension -- pass it as an argument, return it from a
function, use it as part of a larger expression (e.g. concatenate
several lists), and so on.  Getting values to assigne to variables
is just one of the many normal ways in which you can use expressions, 
and a list comprehension is an expression.

In languages which draw a distinction between expressions and
statements, and Python is in fact such a language, using an
expression purely for its side effects is normally best avoided,
because that's what statements are for.  There are obvious corner
cases (since Python does not distinguish functions, which return
a value, from procedures, which only have side effects -- in a
language with the expression/statement distinction, it might be
conceptually cleaner to have the procedure/function one as well,
as in Pascal or Fortran, but Python follows C in having the
former but not the latter, drawing boundaries a bit differently),
but that is a reasonable general principle.

> if i *had* a function doing any meaningful with the iterated values
> by itstelf, why bother with the returned list.
> Ex:
> ignore = [ add_to_db(x) for x in my_list if x[:4]=='Hans']
> 
> Looks at least as nice to me as
> 
> for x in my_list:
>      if x[:4]=='Hans':
>          add_to_db(x)
> 
> But there might be reasons one prefers the for loop.

You should also consider other alternatives, such as:

for x in [ x for x in my_list if x.startswith('Hans') ]:
    add_to_db(x)

(use .startswith, not [:4]==, for clarity & to avoid errors)
or even lambda-based ones (preferably with itertools):

for x in itertools.ifilter(lambda x: x.startswith('Hans'), my_list):
    add_to_db(x)

The reason to avoid the list-comprehension is that a LC is a
construct that goes to quite some trouble to collect all the
results of the expression it starts with -- creates and
extends a list just for that, its MAIN purpose -- and when
you DON'T CARE about the MAIN purpose of a construct, using
it anyway is stretching things beyond clarity and legibility.

Want another example?  Another side effect of a LC (in the
pursuit of its MAIN purpose -- creating a new list), besides
the one of looping, is binding the loop's control variable.
It's exceptional that way -- expressions normally don't
bind names... but a LC does.  So, people desperate for a
close transcription of the C idiom

    while((c=nauker())!=flip)
        blaupas(c);

sometimes code "niceties" such as

    while [c for c in [nauker()] if c!=flip]:
        blaupas(c)

as the LC in the while's condition binds name 'c' as well
as giving the list (empty, i.e. false, at termination).
Of course, in this case Python offers a direct construct
to transliterate the idiom into:
    for c in iter(nauker, flip):
        blaupas(c)
but it's not so smooth when nauker takes args, or when the
form of the test is not of the "!=flip" kind.

Anyway, the principle is the same: a LC's main purpose is
to create a new list; therefore, use a LC when you want to 
create a new list, but not when you don't care about said
list and only hanker for a side effect (the looping, or
the binding of control-variable names).


> Back to my original desire:
> I of course did not request to add a new funciton to
> python built ins, I was just asking, if I miss a nice
> idea to do the trick :-)
> 
> sys.stdout.write has the drawbacks:
> - need to import sys, well....
> - does no newline automatically, like print
> - only handles strings
> 
> Again, no real pain all that :-)

Right: as I said, sys.stdout.write('%s\n'%x) is in fact
the equivalent, so the newline and stringification are
trivial issues, easily covered.  If you want to avoid
a separate import statement at all costs, well, when
there's a will there's a way -- you CAN shoehorn it all
into a single longish expression...:

    __import__('sys').stdout.write('%s\n' % x)

whether one would WANT that, of course, is another
issue;-).


Alex





More information about the Python-list mailing list