print("Hello world!"). After that, we go on to learning more
stuff about it like being able to pass any number of arguments or of any type etc. I’m writing this
article to give an idea how deep this rabbit hole goes. Turns out, the
The basic premise of the
Calling it with multiple arguments:
Notice that the two strings,
"world" have a space character printed between them.
It doesn’t have to be strings either:
print(42, "is the answer")
42 is the answer
Let’s look at each of these features in detail and see how they work.
Handling of Multiple Arguments¶
sep= keyword argument.
Let’s look at the following examples:
>>> print("the", "world", "is", "a", "cruel", "place") the world is a cruel place >>> print("the", "world", "is", "a", "cruel", "place", sep="-") the-world-is-a-cruel-place >>> print("the", "world", "is", "a", "cruel", "place", sep="") theworldisacruelplace
In the first example, we don’t explicitly give any value to the
sep= keyword argument. So it takes
it’s default value of the space character
" ". In the second example, we set it to the dash
"-" and we can see in the output that the strings are printed joined by dashes.
In the third example, we set the
sep= to an empty string so the output is all the words printed
consecutively making it a cruel experience to read the text.
sep= argument can be any string, it doesn’t have to be a single character and it can contain
newlines and any other shenanigans too.
print("the", "birds", "in", "the", "sky", sep="\n hammertime\n")
This prints the following mind bogglingly useful output:
the hammertime birds hammertime in hammertime the hammertime sky
Yeah, that’s a useful trick, but please, consider people’s sanity when you do such [email protected]#$.
Handling of non-string types¶
We know that the
str on non-string objects, and
print the result of that call.
Let’s experiment with this. Consider the following class definition, which has just one method, the
__str__. If you are unaware of this method, this is what’s called when
str is applied on an
instance of this class. I won’t go into details of that as that’s not the topic of this article.
class Tantrum: def __str__(self): return "awesome __str__ of object %r" % id(self) print(Tantrum())
The output of running this would be something like (the number in the end would obviously be different if you run this script):
awesome __str__ of object 4508612624
So, what happens if our class doesn’t define an implementation for the
__str__ method? Let’s try
class LazySloth: pass print(LazySloth())
This prints the following output (again, the number in the end would obviously be different for you):
<__main__.LazySloth object at 0x105f327d0>
Turns out that when there’s no implementation for
str on the instance will
still produce some information regarding the instance, which is what we got above.
A neat thing here is that this output is actually what calling
repr on the instance would produce.
So, it looks like
str is falling back to returning the output of
repr, when there’s no
__str__ provided. Let’s confirm this by defining a
class RatInFormals: def __repr__(self): return "a sad overridden __repr__ for instance %r" % id(self) print(RatInFormals())
This prints the following output (again, the number will be different for you):
a sad overridden __repr__ for instance 4313389264
Now we get the output of the overridden
So here’s how it works. The
str on any non-string objects, which returns
the result of the
__str__ method, if available, or the result of calling
repr on the instance,
which in turn returns the result of the
__repr__ method, which results in a generic output unless
overridden (like in the last example above).
This should be case in favor towards spending a few seconds thinking about and writing useful
__str__ methods for your custom types. Someone walking along working with your code later on,
might just print an instance of your class to see what’s in it, and the generic output with the
id is unlikely to be very helpful.
Write to files¶
Another keyword argument accepted by
file=. This can be set to a
file object, in
which case the printing will be done to that file object instead of standard output.
Let’s try writing text to a file using the
with open("outputs.txt", "w") as f: print("Stuff that doesn't show up in standard output", file=f)
Running this script obviously doesn’t print anything to the standard output. Instead, a file “outputs.txt” is created which contains the following text:
Stuff that doesn't show up in standard output
Note that since we are opening the file with mode as
"w", so if a file named “outputs.txt” already
exists in the current folder, it will be overwritten.
sys.stderr object in the
sys module is a file-like object that represents the
standard error. Writing to this file-like object directs it to the standard error stream. This is
similar to the
sys.stdout object which represents the standard output stream, in a
file= keyword argument can be set to
sys.stderr which will print to the standard error
import sys print("stuff going to standard error", file=sys.stderr)
You might not notice any difference from setting the
file= argument in the above script, but if
you are running a terminal emulator / shell that shows standard error in red color, then you’ll be
able to see a difference.
If we don’t set a value explicitly to the
file= argument, the output will be sent to the standard
output. There’s a small note to that point to be observed. In reality, the output will be sent to
sys.stdout file object. Usually, these two are the same. But, of course, we can set
sys.stdout to something else.
Consider the following script which changes the value of
sys.stdout, prints something, and then
restores the value of
sys.stdout to its original value.
1 2 3 4 5 6 7 8 9
import sys original_stdout = sys.stdout with open("out.txt", "w") as f: sys.stdout = f print("trololololol") sys.stdout = original_stdout print("restored")
If we run this script, we’ll only see
restored in the output, but the file
out.txt will be
created with the output from line 6.
A minor point to note here is that it’s probably incorrect to say “the default value of the
sys.stdout“. Since if that were the case, changing the value of
not affect the
None and in that case,
We can verify this by explicitly passing in
None to the
import sys original_stdout = sys.stdout with open("out.txt", "w") as f: sys.stdout = f print("trololololol", file=None) sys.stdout = original_stdout print("restored", file=None)
The above script produces the exact same output as when we didn’t provide the
io.StringIO can be used to create a file object that collects all that is
written to it, and then get it all out as a string. This is useful when calling a function that
prints information using the
sys.stdout with a
io.StringIO instance before calling that
function, and then restore it after. Here’s how this might look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
import io, sys def print_product(a, b): print(a * b) original_stdout = sys.stdout string_io = io.StringIO() sys.stdout = string_io print_product(4, 5) sys.stdout = original_stdout result = string_io.getvalue() print("Result is", result)
In this script, the
print_product function prints the result of the multiplication, instead of
returning it. So to get the result out of it, we replace
sys.stdout with a
and after calling the
print_product function, we get the printed result using the
However, note that a similar operation with binary data using
io.BytesIO is not possible, since
end= keyword argument¶
This is like one of those things that we notice only when it’s taken away. The
print("hello on day 1") print("yeah right on day 2") print("oh to hell with you on day 3")
The output of this script is the following:
hello on day 1 yeah right on day 2 oh to hell with you on day 3
The output from the three
"\n" in our calls to
end= argument to something else, it will replace the
newline in the end of the output from a
Check out the following script for example:
print("Doing awesome stuff... ", end="") # do awesome stuff here print("done")
This script prints the following output:
Doing awesome stuff... done
The output of the two
end= argument to an empty
string. The second call to
A Note about Python 2¶
Python 2 had a
However, Python 2.6 introduced a future import that brought the
from __future__ import print_function line at the start of a Python 2 file would disable
A Sad Imitation¶
Here’s a sad little imitation of the
import sys def sad_print(*args, sep=" ", end="\n", file=None): (sys.stdout if file is None else file).write(sep.join(map(str, args)) + end) sad_print("the answer is", 42)
sad_print function, what we are essentially doing is:
stron all of the provided arguments.
- Join the results of the calls to
strusing the value of
- Concatenate the value of
endto the result of above step.
- Call write on result of point-1, with the result of the above step.
I’m sure the
Python’s standard library has a
pprint module, with a
pprint function that takes one
argument, and prints it prettily.
For example, consider the following script:
from pprint import pprint numbers = [1, 2, 3, 4, 5, 6] print(numbers) pprint(numbers) planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", "Pluto"] print(planets) pprint(planets)
We are calling
pprint on the same list of strings. Let’s look at the output:
[1, 2, 3, 4, 5, 6] [1, 2, 3, 4, 5, 6] ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune', 'Pluto'] ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune', 'Pluto']
As we can see, the output from
pprint is prettified, but only if necessary. In the first case,
where we were printing just six numbers, the output was fine as a single line so
pprint did not
cut it up into several lines. But in the second case, the line ends up too long and it may not be
comfortable on small terminal screens. So, it cuts it up.
pprint module can be useful to prettily print (or formatting) lists and dictionaries. Check
out its official documentation for more information.
We may not use all these features of the