3864
Comment: Remove comments about getting hung up on names
|
5388
Rewrite based on PEP 3000 and execution of sample code
|
Deletions are marked like this. | Additions are marked like this. |
Line 1: | Line 1: |
This page discusses the benefits of replacing the current `print` statement with an equivalent builtin. The `println` function presented below does everything the `print` statement does without requiring any hacking of the grammar, and also makes a number of things significantly easier. | This page discusses the benefits of replacing the current `print` statement with an equivalent builtin. The `write` and `writeln` functions presented below do everything the `print` statement does without requiring any hacking of the grammar, and also make a number of things significantly easier. Guido has made it clear he wants to get rid of the `print` statement in ["Python 3.0"]. This page considers why we want to go that way, and how we can actually get there. It will probably be turned into a PEP at some point. |
Line 5: | Line 7: |
* Keyword argument `sep` allows separator to be changed easily and obviously * Keyword argument `lnterm` allows line terminator to be changed easily and obviously |
* Keyword argument `sep` allows item separator to be changed easily and obviously * Keyword argument `linesep` could optionally allow line separator to be changed easily and obviously |
Line 9: | Line 11: |
* Interacts well with PEP 309's partial function application | * Interacts well with PEP 309's partial function application, and the rest of Python's ability to handle functions |
Line 12: | Line 14: |
The example implementation below shows that creating a function with the desired behaviour is quite straightforward. A problem only arises if we decide we want the builtin to have the name `print`. This seriously complicates transition, because `print` is a reserved word in Python 2.x. Since the `print` statement will be around until Py3K allows us to break backwards compatibility, devising a transition plan that lets programmers 'get ready early' for the Py3K transition becomes a significant challenge. | The example implementation below shows that creating a function with the desired behaviour is quite straightforward. However, calling the builtin `print` is a problem due to the fact that `print` is a reserved word in Python 2.x. Since the `print` statement will be around until Py3K allows us to break backwards compatibility, devising a transition plan that lets programmers 'get ready early' for the Py3K transition becomes a significant challenge. |
Line 14: | Line 16: |
If, on the other hand, the builtin had a different name (such as `println`), it would be quite feasible to introduce it during the 2.x series, with the only change in Py3K being the final removal of the `print` statement. | If, on the other hand, the builtin has a different name, it is quite feasible to introduce it during the 2.x series. In [http://www.python.org/peps/pep-3000.htm PEP 3000], it is suggested that the `print` statement be replaced by two builtins: `write` and `writeln`. These names are used in the example below. By using alternative names, and providing the builtins in the 2.x series, it is possible to 'future-proof' code against the removal of the `print` statement in Py3k. This technique of having two printing operations is not uncommon - Java has both `print` and `println` methods, and C# has `Write` and `WriteLine`. Some other names for the builtins which have been suggested are: * `print` - excellent name, but causes severe transition problems as described above * `println` - avoids the transition problems, reflects default behaviour of adding a line, matches Java method name * `printline` - similar to `println`, but avoids the somewhat cryptic abbreviation * `writeline` - similar to `writeln`, but avoids the somewhat cryptic abbreviation * `out` - not a verb, and converting to it may be problematic due to shadowing by variable names * `output` - nice symmetry with input, but using the term as a verb is not typical |
Line 17: | Line 29: |
This is a Python 2.4 compatible sample implementation, which is why it uses the name `println` rather than `print`. | This is a Python 2.4 compatible sample implementation. This version of `writeln` doesn't provide a `linesep` keyword argument in order to keep things simple. |
Line 20: | Line 32: |
def println(*args, **kwds): | def write(*args, **kwds): |
Line 23: | Line 35: |
>>> println(1, 2, 3) 1 2 3 >>> println(1, 2, 3, sep='') 123 >>> println(1, 2, 3, sep=', ') 1, 2, 3 >>> println(1, 2, 3, lnterm='Alternate line terminator\n') 1 2 3Alternate line terminator >>> import sys >>> println(1, 2, 3, stream=sys.stderr) 1 2 3 >>> println(*range(10)) 0 1 2 3 4 5 6 7 8 9 >>> println(*(x*x for x in range(10))) 0 1 4 9 16 25 36 49 64 81 |
This function does NOT automatically append a line separator (use writeln for that) |
Line 39: | Line 37: |
# Nothing to do if no positional arguments if not args: return |
|
Line 40: | Line 41: |
defaults = { | kwd_values = { |
Line 42: | Line 43: |
"lnterm": "\n", | |
Line 45: | Line 45: |
for name, default in defaults.items(): item = None |
for name, default in kwd_values.items(): |
Line 50: | Line 49: |
pass if item is None: kwds[name] = default sep, lnterm, stream = kwds["sep"], kwds["lnterm"], kwds["stream"] |
continue del kwds[name] if item is not None: kwd_values[name] = item if kwds: raise TypeError("%s is an invalid keyword argument for this function" % kwds.keys()[0]) sep, stream = kwd_values["sep"], kwd_values["stream"] |
Line 55: | Line 57: |
for arg in args[:1]: stream.write(str(arg)) |
stream.write(str(args[0])) |
Line 60: | Line 61: |
stream.write(lnterm) | def writeln(*args, **kwds): """Functional replacement for the print statement >>> writeln(1, 2, 3) 1 2 3 >>> writeln(1, 2, 3, sep='') 123 >>> writeln(1, 2, 3, sep=', ') 1, 2, 3 >>> import sys >>> writeln(1, 2, 3, stream=sys.stderr) 1 2 3 >>> writeln(*range(10)) 0 1 2 3 4 5 6 7 8 9 >>> writeln(*(x*x for x in range(10))) 0 1 4 9 16 25 36 49 64 81 """ # Perform the print operation without building the whole string write(*args, **kwds) write("\n", **kwds) |
Line 64: | Line 85: |
These are some comparisons of current print statements with the equivalent code using the builtin. | These are some comparisons of current `print` statements with the equivalent code using the builtins `write` and `writeln`. |
Line 66: | Line 87: |
'''Standard printing:''' | |
Line 68: | Line 88: |
# Standard printing | |
Line 69: | Line 90: |
println(1, 2, 3) | writeln(1, 2, 3) # Printing without any spaces print "%d%d%d" % (1, 2, 3) writeln(1, 2, 3, sep='') # Print as comma separated list print "%d, %d, %d" % (1, 2, 3) writeln(1, 2, 3, sep=', ') # Print without a trailing newline print 1, 2, 3, write(1, 2, 3) # Print to a different stream print >> sys.stderr, 1, 2, 3 writeln(1, 2, 3, stream=sys.stderr) # Print a simple sequence print " ".join(map(str, range(10))) writeln(*range(10)) # Print a generator expression print " ".join(str(x*x) for x in range(10)) writeln(*(x*x for x in range(10))) |
Line 71: | Line 116: |
'''Printing without any spaces:''' {{{#!python print "%d%d%d" % (1, 2, 3) println(1, 2, 3, sep='') }}} '''Print as comma separated list:''' {{{#!python print "%d, %d%, d" % (1, 2, 3) println(1, 2, 3, sep=', ') }}} '''Print without a trailing newline:''' {{{#!python print 1, 2, 3, println(1, 2, 3, lnterm='') }}} '''Print to a different stream:''' {{{#!python print >> sys.stderr, 1, 2, 3 println(1, 2, 3, stream=sys.stderr) }}} '''Print a simple sequence:''' {{{#!python print " ".join(map(str, range(10))) println(*range(10)) }}} '''Print a generator expression:''' {{{#!python print " ".join(str(x*x) for x in range(10)) println(*(x*x for x in range(10))) }}} |
This page discusses the benefits of replacing the current print statement with an equivalent builtin. The write and writeln functions presented below do everything the print statement does without requiring any hacking of the grammar, and also make a number of things significantly easier.
Guido has made it clear he wants to get rid of the print statement in ["Python 3.0"]. This page considers why we want to go that way, and how we can actually get there. It will probably be turned into a PEP at some point.
Benefits of using a function instead of a statement
- Extended call syntax provides better interaction with sequences
Keyword argument sep allows item separator to be changed easily and obviously
Keyword argument linesep could optionally allow line separator to be changed easily and obviously
Keyword argument stream allows easy and obvious redirection
- The builtin can be replaced for application wide customisation (e.g. per-thread logging)
- Interacts well with PEP 309's partial function application, and the rest of Python's ability to handle functions
Getting there from here
The example implementation below shows that creating a function with the desired behaviour is quite straightforward. However, calling the builtin print is a problem due to the fact that print is a reserved word in Python 2.x. Since the print statement will be around until Py3K allows us to break backwards compatibility, devising a transition plan that lets programmers 'get ready early' for the Py3K transition becomes a significant challenge.
If, on the other hand, the builtin has a different name, it is quite feasible to introduce it during the 2.x series. In [http://www.python.org/peps/pep-3000.htm PEP 3000], it is suggested that the print statement be replaced by two builtins: write and writeln. These names are used in the example below. By using alternative names, and providing the builtins in the 2.x series, it is possible to 'future-proof' code against the removal of the print statement in Py3k.
This technique of having two printing operations is not uncommon - Java has both print and println methods, and C# has Write and WriteLine.
Some other names for the builtins which have been suggested are:
print - excellent name, but causes severe transition problems as described above
println - avoids the transition problems, reflects default behaviour of adding a line, matches Java method name
printline - similar to println, but avoids the somewhat cryptic abbreviation
writeline - similar to writeln, but avoids the somewhat cryptic abbreviation
out - not a verb, and converting to it may be problematic due to shadowing by variable names
output - nice symmetry with input, but using the term as a verb is not typical
Sample implementation
This is a Python 2.4 compatible sample implementation. This version of writeln doesn't provide a linesep keyword argument in order to keep things simple.
1 def write(*args, **kwds):
2 """Functional replacement for the print statement
3
4 This function does NOT automatically append a line separator (use writeln for that)
5 """
6 # Nothing to do if no positional arguments
7 if not args:
8 return
9 # Parse the keyword-only optional arguments
10 kwd_values = {
11 "sep": " ",
12 "stream": sys.stdout,
13 }
14 for name, default in kwd_values.items():
15 try:
16 item = kwds[name]
17 except KeyError:
18 continue
19 del kwds[name]
20 if item is not None:
21 kwd_values[name] = item
22 if kwds:
23 raise TypeError("%s is an invalid keyword argument for this function" % kwds.keys()[0])
24 sep, stream = kwd_values["sep"], kwd_values["stream"]
25 # Perform the print operation without building the whole string
26 stream.write(str(args[0]))
27 for arg in args[1:]:
28 stream.write(sep)
29 stream.write(str(arg))
30
31 def writeln(*args, **kwds):
32 """Functional replacement for the print statement
33
34 >>> writeln(1, 2, 3)
35 1 2 3
36 >>> writeln(1, 2, 3, sep='')
37 123
38 >>> writeln(1, 2, 3, sep=', ')
39 1, 2, 3
40 >>> import sys
41 >>> writeln(1, 2, 3, stream=sys.stderr)
42 1 2 3
43 >>> writeln(*range(10))
44 0 1 2 3 4 5 6 7 8 9
45 >>> writeln(*(x*x for x in range(10)))
46 0 1 4 9 16 25 36 49 64 81
47 """
48 # Perform the print operation without building the whole string
49 write(*args, **kwds)
50 write("\n", **kwds)
Code comparisons
These are some comparisons of current print statements with the equivalent code using the builtins write and writeln.
1 # Standard printing
2 print 1, 2, 3
3 writeln(1, 2, 3)
4
5 # Printing without any spaces
6 print "%d%d%d" % (1, 2, 3)
7 writeln(1, 2, 3, sep='')
8
9 # Print as comma separated list
10 print "%d, %d, %d" % (1, 2, 3)
11 writeln(1, 2, 3, sep=', ')
12
13 # Print without a trailing newline
14 print 1, 2, 3,
15 write(1, 2, 3)
16
17 # Print to a different stream
18 print >> sys.stderr, 1, 2, 3
19 writeln(1, 2, 3, stream=sys.stderr)
20
21 # Print a simple sequence
22 print " ".join(map(str, range(10)))
23 writeln(*range(10))
24
25 # Print a generator expression
26 print " ".join(str(x*x) for x in range(10))
27 writeln(*(x*x for x in range(10)))