Advanced print() Function Tutorial and Techniques for Python Developers


Python is the most popular programming language which is widely used for a variety of applications, from Web Development to Scientific Computing, Artificial Intelligence, Machine Learning to Quantum Computing, and Data Science. What makes it so attractive for students, data scientists and programmers is its simplicity, readability, and flexibility, and of course, its large and active community contributes to its growth and development.


print("Hello World!")

One of the most commonly used functions of Python is the print() function, which allows developers to output text to the console. The print() function may seem simple at first glance, but there are many advanced techniques and features that can help developers get the most out of it.


In this tutorial, we will explore advanced techniques and features of the print() function in Python in depth. We will cover different ways to format output, advanced usage of the function, tips for debugging with print(), common mistakes to avoid, and alternative print functions available in Python

Table of Contents

  1. What is print() in Python?
  2. Understanding the syntax of print() function
  3. Deep-dive into print function Parameters with Examples
  4. Examples with a combination of all parameters of print function
  5. Advanced formatting using the print function.
  6. Introduction: f-strings with print function
  7. Common Errors/Exception with print function
  8. More advanced usage of print function with python modules


What is print() in Python?

    The print() function is used to output text to the console.


    It is a built-in function, which means that it is available by default and does not require any special setup or a need to import of any special module. You can use the print() function to output simple text messages, variable values, and complex data structures to the console, making it an essential tool for debugging and development.



    The print() function in Python has several parameters that allow developers to customize the output.


    Signature of the print() function as of Python version 3.11 :


    print(*objects, sep=' ', end='\n', file=None, flush=False)

    Parameter Usage More Information
    *objects : A variable-length argument that represents the objects to be printed, here * indicates that there can be one or more objects. Multiple objects separated by commas can be passed in. This is also known as the unpacking operator. *objects parameter was introduced in Python version 2.0. Before that, a fixed number of objects had to be passed to the print() function by putting them into a tuple or list.
    sep='' : Specifies the separator between the objects that are printed. The default is a space character (' '), but a custom separator value can be specified. sep parameter was introduced in Python version 3.0. Before that, the default behavior was to end the output with a newline character.
    end='\n' : Specifies the output stream. By default, it is set to None, which means that the output will be printed on the console. The value of the file object can be changed to redirect the output to a file. Like sep end parameter was also introduced in Python version 3.0, and before that, the default separator was a space character.
    file=None : Specifies the output stream. By default, it is set to None, which means that the output will be printed on the console. The value of the file object can be changed to redirect the output to a file. file parameter was introduced in Python version 2.0. Before that, the only way to redirect the output of print() was to make use of the sys.stdout object.
    flush : Specifies whether the output stream should be flushed or not. By default, it is set to False, so the stream is not flushed. If set to True, the output will be flushed immediately after it is written to the stream. flush parameter was introduced in Python recently in the 3.3 version. Earlier to this version, the output buffer was automatically flushed whenever a newline character was encountered.

    The official documentation defines the print function as follows.


    "Print objects to the text stream file, separated by sep and followed by end. sep, end, file, and flush, if present, must be given as keyword arguments."

    Reference: https://docs.python.org/3/library/functions.html#print


Deep Dive into print function Parameters with Examples

    The previous section provided an overview of each parameter of the print function and its purpose, but it can still be a bit unclear how they all work together without some practical examples. Therefore, let's take a deep dive by exploring some code examples that use a permutation and combination of these parameters to get a better understanding of how they work together..

    1. Parameter *object

    Usage: To represent the objects to be printed.


    Explanation: It is a variable-length argument that allows one or more objects to be passed to the print() function, separated by commas. It is also known as the unpacking operator.


    Examples of usage of *object parameter:

    1. Printing single object:

    print("Hello World!")

    Hello World!


    2. Printing multiple objects:

    print('blueberries', 'cranberries', 'strawberries', 'raspberries', 'blackberries', 'grapes')

    blueberries cranberries strawberries raspberries blackberries grapes


    3. Printing multiple objects passed as arguments

    name = "Andrew"
    age = 19
    location = "New York"
    
    print(name, age, location)

    Andrew 19 New York


    4. Printing a list using * operator

    fruits = ["pawpaw", "persimmon", "chokeberry"]
    print(*fruits)

    pawpaw persimmon chokeberry

    Note: print(*fruits) and print(fruits) are not the same.


    print(*fruits) will print each element of the iterable fruits as separate arguments, with a space separator between them. Here as we have not passed any custom sep value it is ' ' (space).


    print(fruits) will print the entire iterable fruits as a single object, without any separator between the elements.


    print(*fruits)

    pawpaw persimmon chokeberry

    print(fruits) 

    ['pawpaw', 'persimmon', 'chokeberry']


    2. Seperator parameter: sep=''

    Usage: Specifies the separator between objects.


    Explanation: Allows you to change the default behavior of the separator, which is a space character (' ').


    Examples of usage of sep parameter


    We will build on the same examples so its easier to co-relate.


    1. Use tab as a seperater instead of space.

    print("Hello","World","!", sep='\t')

    Hello    World    !


    2. Use | (pipe) as a seperater instead of space.

    print('blueberries', 'cranberries', 'strawberries', 'raspberries', 'blackberries', 'grapes', sep='|')

    blueberries|cranberries|strawberries|raspberries|blackberries|grapes


    3. Using "," (comma) separator.

    name = "Andrew"
    age = 19
    location = "New York"
    
    print(name, age, location, sep=",")

    Andrew,19,New York

    Note that there is no space between the commas!


    4. Using the * operator and separator as a semicolon (;).

    fruits = ["pawpaw", "persimmon", "chokeberry"]
    print(*fruits, sep=";")

    pawpaw;persimmon;chokeberry


    3. Parameter 'end'

    Usage: Specifies the character that will be printed at the end of the print function output.


    Explanation: Change the default behavior, it is set to a newline character \n.


    Examples of usage of end parameter:


    1. Change the end separator as ! instead of \n

    print("Hello ","World",end='!')

    Hello World!


    2. Change the end separator to a comma instead of \n

    print('blueberries', 'cranberries', 'strawberries', 'raspberries', 'blackberries', 'grapes', end=',')

    blueberries cranberries strawberries raspberries blackberries grapes,


    3. Change the end parameter to a dot instead of \n

    name = "Andrew"
    age = 19
    location = "New York"
    
    print(name, age, location, end=".")

    Andrew 19 New York.


    4. Change the end parameter to semi-colon

    fruits = ["pawpaw", "persimmon", "chokeberry"]
    print(*fruits, end=";")

    pawpaw persimmon chokeberry;


    4. Parameter 'file'

    Usage: Specifies the output stream.


    Explanation: By default, set to None, which prints the output on the console. We will change the value of the file object to redirect the output to a file.


    Examples of usage of file parameter:


    1. Print the output to a text file.

    print("Hello World!", file=open("hello.txt", "w"))

    2. Write the output of the print function to a .csv file.

    print('blueberries', 'cranberries', 'strawberries', 'raspberries', 'blackberries', 'grapes', file=open("fruits.csv", "w"))

    5. Parameter "flush"

    Usage: Specifies whether the output stream should be flushed or not.


    Explanation: By default, it is set to False, which means the stream is not flushed. we will see examples where we set it to True, so that the output will be flushed immediately after it is written to the stream.

    Example:
    import time
    
    for i in range(5):
        print(f"Task {i+1}", end=' - ')
        time.sleep(1)
        print('completed!', flush=True)
    Python Print Function with Flush as True Example

    As we have set the flush parameter as True, each output is immediately written to the console as soon as it is generated. Its important to note here is without flush=True, the output would be buffered until the buffer is full or a newline character is encountered.


Examples with a combination of all parameters of print function

    I am sure that now we understand all the parameters of the print function in Python. It is time to take a look at them working together as real-life use cases.


    1. Using objects and variables and creating a sentence.

    name = "Andrew"
    age = 19
    city = "New York"
    
    print("My name is", name, "and I am", age, "years old, and I live in ",city, end=".", sep=" ", file=open("message.txt", "w"))

    My name is Andrew and I am 19 years old, and I live in New York.

    2. Creating a formatted table in Console

    names = ["Sam", "Dean", "Bobby", "Castiel", "Crowley"]
    ages = [32,36,63,42,55]
    locations = ["Chicago","Texas", "New York", "California","North Carolina"]
    end_str = "\n-------------------------------\n"
    sep_str ="\t"
    
    # printing in table format with proper indentation
    print("Name","Age","Location", sep=sep_str,end=end_str)
    print(names[0], ages[0], locations[0], sep=sep_str,end=end_str)
    print(names[1], ages[1], locations[1], sep=sep_str,end=end_str)
    print(names[2], ages[2], locations[2], sep=sep_str,end=end_str)
    print(names[3], ages[3], locations[3], sep=sep_str,end=end_str)
    print(names[4], ages[4], locations[4], sep=sep_str,end=end_str)

    Name    Age    Location
    -----------------------------------
    Sam    32    Chicago
    -----------------------------------
    Dean    36    Texas
    -----------------------------------
    Bobby    63    New York
    -----------------------------------
    Castiel    42    California
    -----------------------------------
    Crowley    55    North Carolina
    -----------------------------------

    If looking at the output table you are wondering it does not look to be printing the table format with proper indentation! Well you are writing, this is what we will be covering in the next section.


    Advanced formatting using print function example:
    print(f"{'Name':>10}{sep_str}{'Age':>10}{sep_str}{'Location':>15}", end=end_str)
    print(f"{names[0]:>10}{sep_str}{ages[0]:>10}{sep_str}{locations[0]:>15}", end=end_str)
    print(f"{names[1]:>10}{sep_str}{ages[1]:>10}{sep_str}{locations[1]:>15}", end=end_str)
    print(f"{names[2]:>10}{sep_str}{ages[2]:>10}{sep_str}{locations[2]:>15}", end=end_str)
    print(f"{names[3]:>10}{sep_str}{ages[3]:>10}{sep_str}{locations[3]:>15}", end=end_str)
    print(f"{names[4]:>10}{sep_str}{ages[4]:>10}{sep_str}{locations[4]:>15}", end=end_str)
    Output:
          Name	       Age	       Location
    ---------------------------------------------------
           Sam	        32	        Chicago
    ---------------------------------------------------
          Dean	        36	          Texas
    ---------------------------------------------------
         Bobby	        63	       New York
    ---------------------------------------------------
       Castiel	        42	     California
    ---------------------------------------------------
       Crowley	        55	 North Carolina
    ---------------------------------------------------

Introduction: f-strings

    PEP 498 a feature introduced in Python 3.6 is a new way of formatting strings known as "Literal String Interpolation", or more commonly referred to as f-strings. Using f-strings, you can embed expressions directly inside string literals by prefixing them with the letter 'f'. This makes for a very concise and readable way of formatting strings that can greatly improve the readability and maintainability of your code.

    Syntax:

    f"Some String Literal {expression-1} more text {expression-2} ..."

    The f-strings literal string expressions are evaluated at run-time, and they are expressed with curly braces as shown above.

    Example:

    name = "Andrew"
    age = 19
    city ="New York"
    message = f"My name is {name} and I am {age} years old. I live in {city}."
    print(message)

    My name is Andrew and I am 19 years old. I live in New York.

    f-strings are a powerful tool to make it easy for you to create complex strings with embedded expressions. Another reason to make use of f-strings is they are highly readable thus making it easy to understand the meaning of the string without having to mentally parse complex formatting codes.


    The old way of %-strings and why not to use it.

    message = "My name is %s and I am %d years old. I live in %s." % (name, age, city)
    print(message)

    As you can see, the %-string format is an old-school way of formatting print messages and it requires a lot of placeholders, the values have to be passed in a separate tuple. It can be hard to understand and maintain for complex strings with multiple placeholders so should be avoided.

String interpolation using f-strings

    String interpolation in Python is a way to embed expressions inside string literals, it allows you to construct strings that include variable values, expressions, and function calls.

    name = "Andrew"
    age = 19
    city = "New York"
    
    num_to_word = { 19:"nineteen" }
    
    message = f"My name is {name} and I am {num_to_word[age]} years old. I live in {city}."
    print(message)

    My name is Andrew and I am nineteen years old. I live in New York.

    In the above example, we are using f-strings interpolation to create a message string that includes variable values and a dictionary lookup.


Formatting of variables and expressions within f-strings

    String formatting is a very essential part of any programming language, and Python offers the most advanced ways to format strings.

    In the below table, we explore the syntax and usage of various formatting options available within f-strings.


    SyntaxDescriptionExampleOutput
    {}Positional Argumentf"My name is {}, I am {} years old".format(name, age)My name is Andrew, I am 19 years old
    {index}Positional Argument with Indexf"My name is {0}, I am {1} years old".format(name, age)My name is Andrew, I am 19 years old
    {var}Variable Namef"My name is {name}, I am {age} years old"My name is Andrew, I am 19 years old
    {expr}Expressionf"Next year, I will be {age+1} years old"Next year, I will be 20 years old
    {var.method()}Methodf"My name is {name.upper()}, I am {age} years old"My name is ANDREW, I am 19 years old
    {var.attribute}Attributef"I am from {city.title()}"I am from New York
    {var[index]}Indexed Valuef"My name starts with {name[0]}"My name starts with A
    {var[start:stop]}Slicef"I live in {city[:3]}"I live in New
    {var:{width}}Minimum Widthf"My name is {name:{10}}"My name is Andrew
    {var:{width}.{precision}}Width and Precisionf"I am {age:{5}.{2}} years old"I am 19.00 years old
    {var:!^width}Fill and Alignf"I am {name:!^10}."I am !!Andrew!!
    {var:=^width}Fill and Alignf"I am {name:=$^10}."I am ==Andrew==
    {var:!<width}}Fill and Alignf"I am {name:!>10}."I am !!!!!Andrew

    Let's take our old table example and see how we can format the output as a table in the console using f-strings and various formatting styles.

    names = ["Sam", "Dean", "Bobby", "Castiel", "Crowley"]
    ages = [32, 36, 63, 42, 55]
    locations = ["Chicago", "Texas", "New York", "California", "North Carolina"]
    end_str = "\n-------------------------------\n"
    sep_str = "\t"

    Table data left aligned

    print("Name\t\tAge\tLocation")
    print(end_str)
    
    for i in range(len(names)):
        print(f"{names[i]:<10}{ages[i]:<10}{locations[i]:<15}")
    
    print(end_str)


    Name       Age	    Location
    -----------------------------------
    Sam       32        Chicago        
    Dean      36        Texas          
    Bobby     63        New York       
    Castiel   42        California     
    Crowley   55        North Carolina 
    -----------------------------------

    Table data right aligned

    name="Name"
    age="Age"
    location="Location"
    print(f"{name:>10}{age:>10}{location:>20}")
    print(end_str)
    
    for i in range(len(names)):
        print(f"{names[i]:>10}{ages[i]:>10}{locations[i]:>20}")
    
    print(end_str)


          Name       Age            Location
    -------------------------------------------
           Sam        32             Chicago
          Dean        36               Texas
         Bobby        63            New York
       Castiel        42          California
       Crowley        55      North Carolina
    -------------------------------------------

    Table data center aligned

    print(f"{name:^10}{age:^10}{location:^20}")
    print(end_str)
    
    for i in range(len(names)):
        print(f"{names[i]:^10}{ages[i]:^10}{locations[i]:^20}")
    
    print(end_str)


       Name      Age          Location      
    -------------------------------------------
       Sam        32          Chicago       
       Dean       36           Texas        
      Bobby       63          New York      
     Castiel      42         California     
     Crowley      55       North Carolina   
    -------------------------------------------
    

    As you would have understood, the formatting codes used in the f-strings above are,

    1. < for left alignment.
    2. ^ for center alignment.
    3. > for right alignment.

    You can adjust these values as needed to customize the table formatting to your liking.


    Fill and Align Example:

    for i in range(len(names)):
        print(f"{names[i]:_>10}{ages[i]:,>10}{locations[i]:/>20}")
    
       Name      Age          Location      
    -------------------------------------------
    _______Sam,,,,,,,,32/////////////Chicago
    ______Dean,,,,,,,,36///////////////Texas
    _____Bobby,,,,,,,,63////////////New York
    ___Castiel,,,,,,,,42//////////California
    ___Crowley,,,,,,,,55//////North Carolina
    -------------------------------------------

f-strings with dictionaries example

    person = {'name': 'Andrew', 'age': 19}
    message = f"My name is {person['name']} and I am {person['age']} years old."
    print(message)

    My name is Andrew and I am 19 years old.


Nested f-strings example

name = "Andrew"
age = 19
city = "New York"

message = f"My name is {name} and I am {age} years old. I live in {city}."
nested_message = f"Here's my info: {message}"
print(nested_message)

Here's my info: My name is Andrew and I am 19 years old. I live in New York.


Common errors/exceptions and how to debug f-strings

    NameError

    name = "Andrew"
    age = 19
    message = f"My name is {naem} and I am {age} years old."

    NameError: name 'naem' is not defined

    If you provide an incorrect name for a variable, you will get a NameError.

    Debugging f-String

    name = "Andrew"
    age = 19
    message = f"My name is {name=} and I am {age} years old."
    print(message)

    My name is name='Andrew' and I am 19 years old.

    When you are unsure what the value of a variable is when used within an f-string, you can simply add a = sign next to the variable, this is called debugging the f-string where-in you will see that name of the variable and its value is printed out enclosed within single quotes.


    Error handling using f-string

    try:
        x = 1 / 0
    except ZeroDivisionError as e:
        message = f"An error occurred: {e}"

    You can use f-strings to catch and handle exceptions by wrapping the expression in a try-except block.

    If the code inside the try block raises a ZeroDivisionError, the exception will be caught and the error message will be included in the f-strings.


    Using !r as a Conversion Flag in f-strings to Debug unexpected behaviors

    !r is the conversion flag in Python that is used to represent the value as a string using the repr() method, which returns a printable representation of an object.

    name = "Andrew"
    age = 19
    print(f"My name is {name!r} and I am {age} years old.")
    

    My name is 'Andrew' and I am 19 years old.

    The !r conversion flag can be very useful for debugging f-strings as it shows the printable representation of an object.

    This can really help to identify any unexpected behavior in your code.

    name = "Andrew\n"
    age = 19
    print(f"My name is {name} and I am {age} years old.")
    print(f"My name is {name!r} and I am {age} years old.")

    Example to know if the value contains special characters or escape sequences, using !r can help you see exactly what those characters are.

    My name is Andrew
     and I am 19 years old.

    My name is 'Andrew\n' and I am 19 years old.

    As you can see by adding !r to the name variable in the second print function, we were able to identify why there was a line break.


Common Errors/Exception with print function

    1. Syntax Error:
      print "Hello World!"

      SyntaxError: Missing parentheses in call to 'print'. Did you mean print("Hello, world!")?

      If you forget to add parentheses around the argument, you will get a syntax error. The error stack is quite detailed to let you know that you need to enclose the print calls with parentheses.

      Fix:

      print("Hello World!")

    2. Type Error:
      name ="Andrew"
      age = 19
      print(f"{name} is "+ age + " years old.")

      TypeError: can only concatenate str (not "int") to str

      TypeError occurs when the argument passed to the print() function is not of the correct data type.

      Fix:

      print(f"{name} is "+ str(age) +" years old.")

      As you can see we cast the int value to a string for the concatenation to work.


    3. Name Error:
      name ="Andrew"
      age = 19
      print(f"{name} is {aeg} years old. I live in {my_city}")

      NameError: name 'aeg' is not defined
      NameError: name 'my_city' is not defined

      NameError occurs when a variable is misspelled or undefined and used as an argument in the print() function.

      Fix:

      name ="Andrew"
      age = 19
      my_city = "New York"
      print(f"{name} is {age} years old. I live in {my_city}")

      As you can see we cast the int value to a string for the concatenation to work.


    4. Indentation Error:
      name ="Andrew"
      age = 19
      
      if age > 18:
      print(f"{name} are an adult.")

      IndentationError: expected an indented block

      This is Python 101 error! If you do not indent your code right, you will get IndentationError.

    5. Unicode Encode Error:
      print("Andrew likes french café! ☕️")

      
      >>> print("Andrew likes french café! ☕️".encode('ascii'))
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      UnicodeEncodeError: 'ascii' codec can't encode character '\u0301' in 
      position 18: ordinal not in range(128)
      

      Fix:

      name ="Andrew"
      age = 19
      
      if age > 18:
        print(f"{name} are an adult.")

      If your string or value of variables used within a print function has UTF-8 characters, you need to encode the print function.

      Fix:

      print("Andrew likes french café! ☕️").encode('utf-8'))

      You would not get this error if you are using Python 3+ as all strings in Python 3 and above work with UTF-8 characters.


      In Python 2, a u prefix was used to denote Unicode strings.

      print(u"Andrew likes french café! ☕️")

More advanced usage of print function with Python modules

Custom print function using functools module

    If you have a special need to change the custom behavior of the built-in print function in Python, you can make use of the functools module to achieve this.

    import functools
    
    custom_print = functools.partial(print, sep='-', end='|', file=open('console.log', 'a'))
    
    custom_print("This","is","a","custom","print","example")

    As you can see we have set custom values for sep, end, and file parameters. This could be something one may use for custom logging using print function.

Coloring the print fuction output

    from termcolor import colored
    
    print(colored('Hello, world!','white','on_blue'))

You can make use of the module termcolor (will need to pip install) to color code the print output in the console, you can do a lot of stuff here like.


Some of the features provided by the termcolor module:

  • Colored text output in the terminal using ANSI escape sequences
  • Support for different text attributes, such as bold, underline, and blink
  • Support for different foreground and background colors
  • Easy to use API for coloring text and printing it to the terminal
  • Support for nested colored text using the colored() function
  • Compatibility with both Python 2 and Python 3

Progress bar using tqdm module

The tqdm module is a third-party library that provides an easy way to add progress bars to Python code. It is commonly used in loops or when iterating over large datasets to keep track of how much time is left until the task is completed.

Some of the features provided by the tqdm module:

  • Progress bars for loops and iterable objects
  • Customizable progress bar styles and appearance options
  • Dynamic progress meter
  • Display of elapsed time, estimated time remaining, and progress percentage
  • Optional smoothing of progress updates
  • Support for nested progress bars
  • Integration with other third-party libraries and tools

Example: Progress status using print function

from tqdm import tqdm
import time

for i in tqdm(range(10)):
    print(f"Building AI Model {i}")
    time.sleep(1)

print("AI Model is ready to use!")
 0%|          | 0/4 [00:00<?, ?it/s] Building AI Model 0
 25%|██▌       | 1/4 [00:01<00:03,  1.00s/it] Building AI Model 1
 50%|█████     | 2/4 [00:02<00:02,  1.00s/it] Building AI Model 2
 75%|███████▌  | 3/4 [00:03<00:01,  1.00s/it] Building AI Model 3
100%|██████████| 4/4 [00:04<00:00,  1.00s/it] AI Model is ready to use!

Summing what we learnt

  1. The print function is used to display output in the console.
  2. It takes one or more objects as input, which are separated by a comma by default.
  3. The separator can be changed using the sep parameter.
  4. The end character can be changed using the end parameter.
  5. The output can be redirected to a file using the file parameter.
  6. The flush parameter can be used to force the output to be immediately written to the console or file.
  7. f-strings are a new way to format strings in Python that allow you to embed expressions and variables inside curly braces {}.
  8. Nested f-strings can be used to format multiple variables or expressions in a single string.
  9. Conversion flags can be used inside f-strings to change how variables are displayed, such as using the !r flag to display a printable representation of an object.
  10. The logging module can be used for more advanced logging instead of using print statements.
  11. functools.partial can be used to create a customized print function with preset arguments.
  12. The termcolor module can be used to colorize text in the console.
  13. The progress bar can be created using the tqdm module to show the progress of long-running tasks.
  14. Errors can occur with the print function due to syntax, type, name, and Unicode encoding issues.
  15. Debugging techniques, such as using the repr function or printing variables, can be used to fix errors in the print function.