Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

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?

+3
−0

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?

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.
Why should this post be closed?

0 comment threads

1 answer

+3
−0

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 prints 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
History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

0 comment threads

Sign up to answer this question »