Please hear my plea: print without softspace

S

Stephen Horne

'print' is not a shortcut for 'sys.stdout.write'.
'print' is a statement. 'sys.stdout.write' is a function.
You can't define a shortcut for a statement.
--dang

To the best of my knowledge, the word 'shortcut', is in common
idiomatic use in English and refers to a quick and convenient means of
achieving the same goal, by obvious analogy to the literal meaning of
shortcut as a route to a given destination which is shorter than the
normal route.

The fact that in this case the means happens to be a statement as
opposed to a function doesn't seem relevant to me. And the fact that
programmers cannot define their own custom shortcuts in this way seems
equally irrelevant.

Actually, your protest seems analogous to saying 'that isn't a
shortcut to the highstreet because its a back alley - the normal route
is a proper road'. In literal shortcuts, the classification of roads,
back alleys, dirt tracks etc is irrelevant, and I see no reason to
fuss about technicalities such as the type of syntax in an idiomatic
use.

Similarly, I cannot build my own custom dirt tracks and back alleys,
yet I can still refer to existing ones as shortcuts.

If there was some specialist definition of the word 'shortcut' in
Python I might agree that David had made a poor choice of words, but
I'm not aware of any and even if there is that doesn't make normal
English usage invalid.

'print' is a quick and convenient way of doing much the same thing as
'sys.stdout.write'. It's a shortcut. I honestly can't see any logic in
claiming otherwise.

Even those unwanted spaces can be seen as analogous to the muck you
get all over your shoes in many literal shortcuts ;-)
 
S

Stephen Horne

^^^
Ahem, (str(arg))

Ahem part two - too many spaces, as you add one after the last arg.


def print (*args) :
for arg in args[:-1] :
sys.stdout.write (str (arg))
sys.stdout.write (" ")

if len(args) > 0 :
sys.stdout.write (str (args [-1]))

sys.stdout.write ("\n")


....or...


def print (*args) :
sys.stdout.write (string.join ([str(i) for i in args], " ") + "\n")
 
D

David MacQuigg

def print (*args) :
sys.stdout.write (string.join ([str(i) for i in args], " ") + "\n")

Cool. But we can go one better. The join function requires that we
import the soon-to-be-deprecated ( I am told.) string module. The
proper way ( I am told.) is to use the join *method*:
def prnt(*args): sys.stdout.write(' '.join([str(i) for i in args]) + '\n')
prnt(1,2,3) 1 2 3

Actually, I think this is rather ugly. The string 'method' still looks
like a 'function'. We really need a 'join' method that takes a list
of strings as its 'front-door' object and the 'joiner' as its optional
'side-door' input. Then we could do like Ruby and say:

[str(i) for i in args].join(' ').append('\n')

For another good example of Ruby's advantage in sequential operations
on strings see http://userlinux.com/cgi-bin/wiki.pl?RubyPython

Incidentally, sys.stdout.write(i) *will* work on integers (at least in
Python 2.3.3 running in IDLE 1.0.2) It doesn't work if I start Python
from a command line!! Very strange. I do run my examples before
posting.

-- Dave
 
T

Terry Reedy

Depends on how fussy one is being about the meaning of 'shortcut'.
'Syntactic sugar' is another way to put it.
I'm not using the word 'shortcut' very precisely.

I think you were precise enough for informal clp discussion with no
retraction needed.
Mark Lutz says what
I mean more clearly in Learning Python, 2nd ed. p. 143: "the print
statement ... provides a user-friendly interface to the sys.stdout
object, with bit of default formatting."

And Guido said much the same about 5 years ago in clp.

Terry J. Reedy
 
T

Terry Reedy

David MacQuigg said:
Actually, I think this is rather ugly. The string 'method' still looks
like a 'function'. We really need a 'join' method that takes a list
of strings as its 'front-door' object and the 'joiner' as its optional
'side-door' input.

A said in previous discussions, the second parameter of the join function
is *any* iterable of strings, and not just lists. Your proposal would
either cripple or omit this generality, depending on whether you meant it
as a replacement of or redundant supplement to the current
joiner.join(iterable_of_strings) method.

Terry J. Reedy
 
M

Martin Bless

[Stephen Horne said:
Easy solution - write your extender such that it takes the original
file in the constructor...

sys.stdout = ExtendedFile (sys.stdout)

Hhm,
.... pass
....
Traceback (most recent call last):

what do you mean?

mb - Martin Bless
 
D

David MacQuigg

A said in previous discussions, the second parameter of the join function
is *any* iterable of strings, and not just lists. Your proposal would
either cripple or omit this generality, depending on whether you meant it
as a replacement of or redundant supplement to the current
joiner.join(iterable_of_strings) method.

I would make it a supplement and treat the issue of deprecation the
same as other string functions which now have a (redundant) method
equivalent. Also, I would keep the same level of generality in the
new method. If 'seq' is a sequence of strings (list or iterable), and
'sep' is the desired separator, we have now:
sep.join(seq)
and I would like to see an equivalent
seq.join(sep)

I just did a search on 'join strings group:comp.lang.python' and came
up with 1200 hits! Looks like I need to do some research before
wading into these waters. I'll post a new topic, if I decide to
pursue this discussion, since we are getting a bit off on this one. I
would also like to see a 'map' method. With 'map' and 'join' methods
we could greatly improve the readability of long sequences of
operations on strings (like Ruby). Again, I need to do some research.

-- Dave
 
D

Dang Griffith

To the best of my knowledge, the word 'shortcut', is in common
idiomatic use in English and refers to a quick and convenient means of
achieving the same goal, by obvious analogy to the literal meaning of
shortcut as a route to a given destination which is shorter than the
normal route.

The fact that in this case the means happens to be a statement as
opposed to a function doesn't seem relevant to me. And the fact that
programmers cannot define their own custom shortcuts in this way seems
equally irrelevant.

I understand your idea. However, the difference between a statement
and a function is exactly my point, and I see it as relevant--if any
language change is to come from this discussion. New functions can be
added via modules, and do not require changes to the language itself.
New statements require changes to the language itself, which is what
the OP is asking for.

The [in]ability to define "custom shortcuts", with regards to
statements, is especially relevant because Python doesn't support it.
Other languages do (Ruby? et al), so someone thinks it's relevant.
It's relevant because if Python *did* support it, this thread wouldn't
be happening. (Or at least would have ended after someone
replied with "oh, just do XYZZY to make a printraw statement".)
Actually, your protest seems analogous to saying 'that isn't a
shortcut to the highstreet because its a back alley - the normal route
is a proper road'. In literal shortcuts, the classification of roads,
back alleys, dirt tracks etc is irrelevant, and I see no reason to
fuss about technicalities such as the type of syntax in an idiomatic
use.

I'm not trying to be pedantic (at least, I think I'm not trying), but
the difference between a statement and a function is a semantic
difference, not (just) a syntactic one.
Similarly, I cannot build my own custom dirt tracks and back alleys,
yet I can still refer to existing ones as shortcuts.

If there was some specialist definition of the word 'shortcut' in
Python I might agree that David had made a poor choice of words, but
I'm not aware of any and even if there is that doesn't make normal
English usage invalid.

'print' is a quick and convenient way of doing much the same thing as
'sys.stdout.write'. It's a shortcut. I honestly can't see any logic in
claiming otherwise.

The logic is that "much the same" is not "the same", and is the point
of the OP's plea.

In any case, I think the one-liner posted satisfies nearly all of the
requirements.

import sys
def pr(*args): sys.stdout.write(''.join([str(i) for i in args]))

Where it fails is the difference between a function and a statement.
The OP said [one of] the advantages of a statement is that there is no
need for parenthesis. This is the reason I brought up the point that
a function and a statement are semantically different, and why none of
the function-based suggestions will satisfy the OP's plea.

--dangout
 
T

Terry Reedy

Dang Griffith said:
I'm not trying to be pedantic (at least, I think I'm not trying),

You further clarification makes this more apparent ;-)
but the difference between a statement and a function is a semantic
difference, not (just) a syntactic one.

If one views statements as an alternate syntax for expressing functions (in
the general math rather than special Python object sense) on the program
state, then the different is just syntactic. For instance,

import somemod # is sematically equal to
somemod = __import__('somemod')

and the statement form can be regarded as a shortcut (syntactic sugur) for
the explicit function form, which requires parens, and duplication with
quotes.

Similarly, after 'import __main__',

name = object # abbreviates the more explicit functional form
setattr(__main__, 'name', object)

Note again the need to quote in the functional form. Most Python
statements do some implicit quoting (as do, for instance, Lisp special
forms and macros). A hypothetical __def__ function, for instance, would
require quoting of the function and parameter names and the whole code
block, but not the default arg expressions.
The logic is that "much the same" is not "the same", and is the point
of the OP's plea.

In any case, I think the one-liner posted satisfies nearly all of the
requirements.

import sys
def pr(*args): sys.stdout.write(''.join([str(i) for i in args]))

Where it fails is the difference between a function and a statement.
The OP said [one of] the advantages of a statement is that there is no
need for parenthesis.

This difference is purely syntax, not sematics in terms of the net effect
on program state, including the contents of the stdout stream.

Perhaps we have different meanings for the word semantics?

Terry J. Reedy
 
S

Stephen Horne

I understand your idea. However, the difference between a statement
and a function is exactly my point, and I see it as relevant--if any
language change is to come from this discussion. New functions can be
added via modules, and do not require changes to the language itself.
New statements require changes to the language itself, which is what
the OP is asking for.

The [in]ability to define "custom shortcuts", with regards to
statements, is especially relevant because Python doesn't support it.
Other languages do (Ruby? et al), so someone thinks it's relevant.
It's relevant because if Python *did* support it, this thread wouldn't
be happening. (Or at least would have ended after someone
replied with "oh, just do XYZZY to make a printraw statement".)

OK - sorry I didn't get that.

I suspect that advocating the ability to define custom statements
would be a serious uphill battle. While it allows things to be written
in a different syntax, it doesn't allow for any semantics that aren't
already supportable through functions.

I do see the attraction, though.

I wonder what would be the implications if the brackets for functions
were made optional when the function is used as a statement (in the
sense of not being within an expression, so that the return value will
be discarded). That is...

printraw (a, b, c, d) # 'printraw' used as normal function
print raw (a, b, c, d) # 'raw' used as normal function


printraw a, b, c, d # legal - brackets optional

print raw a, b, c, d # illegal - brackets required for
# 'raw' as it is used in an
# expression

There is the obvious ambiguity - the parser doesn't know whether you
intend a single tuple argument with the no-brackets form or a
conventional list of parameters in brackets. The obvious workaround is
simply to assume a conventional parameter list until proven otherwise,
requiring that when tuple parameters are used the programmer must
resolve the ambiguity (e.g. by reverting back to normal function
syntax). Alternatively, simply ban tuple displays from being used in
bracket-free function calls completely.

Either way, I suspect that there is too much potential for surprises.
 
S

Stephen Horne

[Stephen Horne said:
Easy solution - write your extender such that it takes the original
file in the constructor...

sys.stdout = ExtendedFile (sys.stdout)

Hhm,
import sys
class MyFile(sys.stdout):
... pass
...
Traceback (most recent call last):

what do you mean?

mb - Martin Bless

I think I was being stupid. The stuff about 'constructor' and
'inheritance' seemed right when I hadn't thought it through, of
course. I was intending inheritence from the file class, but since
when do you specify a particular instance to 'inherit' from in the
constructor???

Probably I need a brain test!

The obvious alternative is to try to add an extra method to
sys.stdout, but this doesn't work out - I assume the dictionary of
attributes for file has been fixed.

Replacing sys.stdout.write doesn't work - how does the replacement
method call the original write once it has been overwritten.

Probably the nearest equivalent is to make a wrapper class, and
perhaps a factory function to create instances wrapping existing
files. Unfortunately, there isn't a UserFile class to make this
easier.
 
D

David MacQuigg

In any case, I think the one-liner posted satisfies nearly all of the
requirements.

import sys
def pr(*args): sys.stdout.write(''.join([str(i) for i in args]))

Where it fails is the difference between a function and a statement.
The OP said [one of] the advantages of a statement is that there is no
need for parenthesis. This is the reason I brought up the point that
a function and a statement are semantically different, and why none of
the function-based suggestions will satisfy the OP's plea.

The inconvenience of typing a single set of parens seems trivial to me
in comparison to the extra burden of an additional syntax.

In reading someone else's code, I rely on the *syntactic* difference
between functions and statements to convey possibly different
*symantics*. If it says
print a,b,c
I know exactly what that statement means. If it says
prn(a,b,c)
I know to look for a definition of 'prn'. Note that in this case,
there are *two* syntactic differences, the parens and the keyword.
The keyword difference is essential, because you *can* say
print(a,b,c)
and still be using the built-in print statement (although with a
slightly different result.)

So, do what you like with the parens, but leave the keywords alone.
Beyond this one statement, the idea might work as a general
enhancement. If the first token in a statement is not a keyword, and
the second is not an assignment operator, then look for a function
definition, and don't require parens. This would allow you to say
'printF a,b,c' or 'importR module'
and have these functions/statements do whatever you want.

At this point my vote for adding more syntax to Python would be -1. I
prefer the added readability of a more consistent use of parens over
the convenience of leaving them off. I see the parens as having much
the same role as the unnecessary ':' at the end of some statements.
It clues beginners as to the structure of a statement, and it enhances
readability for experts.

Now if we want to talk about getting rid of 'self.' ... :>)

-- Dave
 
P

Paul Rubin

David MacQuigg said:
In any case, I think the one-liner posted satisfies nearly all of the
requirements.

import sys
def pr(*args): sys.stdout.write(''.join([str(i) for i in args]))

"import sys" makes it a two-liner, I think.
 
S

Stephen Horne

In reading someone else's code, I rely on the *syntactic* difference
between functions and statements to convey possibly different
*symantics*. If it says
print a,b,c
I know exactly what that statement means. If it says
prn(a,b,c)
I know to look for a definition of 'prn'.

Technically debatable. I doubt that I'd recognise _all_ built-in
functions on sight, for instance, and they need to be looked up in the
Python manual just as statements do. And if you can remember all
built-in functions, then why not memorise all statements?
The keyword difference is essential, because you *can* say
print(a,b,c)
and still be using the built-in print statement (although with a
slightly different result.)

Yes - that's why I mentioned tuple-related problems.
I
prefer the added readability of a more consistent use of parens over
the convenience of leaving them off.

I'm not sure that 'consistent use' is a reasonable argument with
parens. They are used for a number of things - function calls,
precedence override, tuples (yes, I know the 'it's the commas that
make it a tuple' argument, but that doesn't work for the empty tuple
at least), argument lists in 'def' and inheritance in 'class' are the
most obvious off the top of my head. I'm sure there's another odd bit
of syntax or two where brackets are needed if I put some thought into
it.

A little less 'overloading' of brackets could arguably be a good
thing.

That said, I don't necessarily disagree with you - I'm mostly just
curious what the implications would be.
Now if we want to talk about getting rid of 'self.' ... :>)

Not sure about that one. I have used C++ a lot, which means that it
does sometimes bug me. But OTOH...

The name 'self' is just a convention. You are free to call it what you
want.

In C++, a very common naming convention suggests using an 'm_' prefix
for all member variable names. I got the habit of using 'f_' instead
(standing for field) a long time ago and it stuck, but that probably
just means I'm wierd. Anyway, this is really just an extension of an
old C convention where parameters get a 'p' or 'p_' prefix, locals get
and 'l' or 'l_' prefix etc.

Changing the underscore to a dot in C++ gives the same result as using
'm' instead of 'self' in Python. OK, semantically you are doing
slightly different things - but in readability terms its much the same
thing. IMO a standard convention is worth more than enough in
readability terms to justify typing 'self' in full, but really what
Python requires is no different to what a lot of experienced C++
programmers tend to do anyway - it's just a clear, explicit indication
of the scope of the identifier you are referencing.
 
D

David MacQuigg

I would make it a supplement and treat the issue of deprecation the
same as other string functions which now have a (redundant) method
equivalent. Also, I would keep the same level of generality in the
new method. If 'seq' is a sequence of strings (list or iterable), and
'sep' is the desired separator, we have now:
sep.join(seq)
and I would like to see an equivalent
seq.join(sep)

I just did a search on 'join strings group:comp.lang.python' and came
up with 1200 hits! Looks like I need to do some research before
wading into these waters. I'll post a new topic, if I decide to
pursue this discussion, since we are getting a bit off on this one. I
would also like to see a 'map' method. With 'map' and 'join' methods
we could greatly improve the readability of long sequences of
operations on strings (like Ruby). Again, I need to do some research.

OK. I spent a few hours reading some old threads, and I see there are
a lot of discussions already on implementation problems, and
aesthetics. I really can't discuss the implementation problems, other
than to say I have faith that if something *should* be done, it *can*
be done, as it *has* been done in other languages. On the aesthetics,
I am totally agnostic. :>) My concern is *usability* for technical
professionals who are non-programmers. If anyone can point me to some
good threads, please do.

As we are getting way off-topic here, I stated a new thread "Need
better string methods". See you there.

-- Dave
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,135
Latest member
VeronaShap
Top