Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
How can I output / display multiple values, with controlled spacing, without building a string?
I know that I can display a single string in the terminal window like print("example")
(or similarly with a string in a variable), and I know how to open text files and write to them.
I also know that I can create a "formatted" string, that contains multiple pieces of information, in a variety of ways. So if I want to output multiple things, obviously there's the option of creating that string and outputting it.
But sometimes for more complex code, it seems impractical to build a string ahead of time. I experimented a little, and found that:
- if I just call
print
multiple times, each value appears on a separate line. - if I use the
write
method of an open file multiple times, the values appear consecutively, without any space in between.
A lot of the time, neither of these is what I want. For example, I might want to separate each new item with a space as they're output. More generally, I'd like to be able to control the formatting.
What options do I have for doing this? Are there any "gotchas" I should be aware of?
1 answer
The following users marked this post as Works for me:
User | Comment | Date |
---|---|---|
Karl Knechtel | (no comment) | Aug 6, 2023 at 05:38 |
Using the write
method
The write
method of a file offers a much more limited interface. It only accepts one argument - a string, for a file opened in text mode - and outputs just what it's given. Any additional desired spacing must be specified explicitly.
Sometimes, this makes it practical to combine approaches: rather than trying to format an entire string to write to the file, format each piece as it is written - for example, to add whitespace or other decoration.
This could look like:
# We have a `cookbook` full of recipes (some data structure),
# and want to write formatted info to the already open `menu` file.
for recipe in cookbook:
menu.write(f'{recipe.title} ${recipe.total_cost * 4:.2f}\n')
# Assume the result we get back from ChatGPT ends with a newline.
menu.write(chatgpt_rewrite_for_marketing(recipe.description))
# Comply with regulatory requirements.
menu.write(f'({recipe.calories} calories)\n')
print
and newlines
Any approach that works with the write
method of a file can also be used with the print
function in Python 3. However, as noted in the question, print
will automatically put a newline after its output by default. To avoid this, change the default using the end
keyword parameter:
>>> print('spam') # the next prompt shows up on the next line.
spam
>>> print('spam', end='') # the next prompt shows up immediately after.
spam>>>
The argument provided for end
can be any string. (It can also be None
, which just gives the default behaviour - i.e., it's the same as using '\n'
.)
print
is flexible
The print
function accepts any number of positional arguments with values to display, one after the other; these values can be of any type - the function will convert them to string as needed (using str
logic).
>>> print(1, 2, {'buckle': 'my'}, 'shoe')
1 2 {'buckle': 'my'} shoe
(Notice that the key and value in the dictionary are output with quotes - because they are actually part of the string created by converting the dictionary to string.)
print
also accepts certain specific keyword-only arguments (they have to be specified by keyword, so that it can know that they aren't part of the output). As noted in the previous section, the end
keyword-only argument controls what is output after the last item. Similarly, the sep
keyword-only argument controls output between each item. As the previous example demonstrates, this defaults to a single space. Using sep=''
squashes the results together:
>>> print(1, 2, {'buckle': 'my'}, 'shoe', sep='')
12{'buckle': 'my'}shoe
(but there is still a space inside the dictionary output, because again, it is part of the string created from the dictionary.)
Keep in mind that nothing prevents using string formatting techniques on individual arguments to print
. Techniques can be mixed and matched freely.
There are two more keyword-only arguments supported by print
, which require separate discussion.
Gotcha: terminal line buffering
When a Python program print
s data in a terminal window, Python itself is not actually responsible for making the text display (and neither is the script). That's handled by the operating system itself, indirectly via a) the "shell" program that interprets the command line and b) the terminal program that creates and maintains the window.
For efficiency reasons, when this text is communicated, it typically goes into a buffer and will only be displayed when a complete line of output (i.e., ending with a newline) is available, or when it gets some other explicit signal.
That means that code like the following will normally not work as intended:
import time
chant = ['spam'] * 5 + ['lovely'] + ['spam'] * 2
for word in chant:
print(word, end=' ')
time.sleep(0.4)
Using end
this way is useful for making a line appear incrementally, but in this example, the text will probably not show up piece by piece - instead, there is a long pause and then it all appears together. This is because of the line buffering.
To avoid the problem, use the (boolean) flush
keyword parameter:
import time
chant = ['spam'] * 5 + ['lovely'] + ['spam'] * 2
for word in chant:
print(word, end=' ', flush=True)
time.sleep(0.4)
This tells the operating system to use the text in the buffer after the print
call is done, even if it doesn't end with a newline.
Displaying in the terminal is just like writing a file
When the script outputs text with print
, normally it does so by sending data to the so-called standard output - where the shell program will detect it and allow the terminal to display it. From the operating system's perspective, the standard output is a file, which Python also represents using the built-in file type. The sys
standard library allows us to see this:
>>> import sys
>>> sys.stdout
<_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
The last keyword parameter for print
is the file
parameter. This defaults to sys.stdout
, as one would expect, but can be substituted with any object that has a write
method (or with None
, to get the default behaviour) In particular, it can be used to produce formatted output for files:
# Re-working the first example.
for recipe in cookbook:
print(recipe.title, f'${recipe.total_cost * 4:.2f}', file=menu)
menu.write(chatgpt_rewrite_for_marketing(recipe.description))
print(f'({recipe.calories} calories)', file=menu)
This version takes advantage of the ability to specify multiple outputs at once, and the default sep
and end
values.
Advanced usage: working with sys.stdout
Of course, it's also possible to call the .write
method of sys.stdout
directly, just as one would with any other file.
It's even possible to replace sys.stdout with another object, which will affect future print
calls. When no file
argument is provided (or if it's None
), print
will look up the current value of sys.stdout
rather than always using the actual standard output.
When doing this, it's best to use another variable to "remember" the previous file object, so that it can be restored later:
>>> old_stdout = sys.stdout
>>> class devnull: # define a fake "file" type that ignores everything.
... def write(self, text):
... pass
...
>>> sys.stdout = devnull() # make and use a fake
>>> print('This will not be seen')
>>> sys.stdout = old_stdout # good thing we remembered!
>>> print('This will be seen')
This will be seen
0 comment threads