OPS435 Python Extra Advanced String formatting

From CDOT Wiki
Revision as of 10:39, 27 September 2017 by Oatley (talk | contribs) (PART 2 - String Formatting Advanced Features)
Jump to: navigation, search

OBJECTIVES

This is not a necessary learning requirement for the course, this extra lab provides information on more powerful string formatting in Python 3.

INVESTIGATION 1: Advanced String Formatting

PART 1 - String Formatting Basic Fundamentals

In Python scripting, using plus signs and commas for string concatenation is very limited and can become messy when when used at length with many values and/or calculations. This section will cover the format() function that can be used with every type of string. This function allows the user of Python to create well formatted code, to align text, and to convert values efficiently and cleanly. While this section uses lists and dictionaries, they contain strings which can be accessed and displayed in a formatted manner.
Perform the Following Steps:
  1. Launch your Centos VM, open a shell terminal (as a regular user) and start a new ipython3 session:
    ipython3
    To start, let's use the format() function on a string, The text .format() is located at the end of a defined string. The format() function can contain arguments or parameters within the parenthesis ( ).

  2. To demonstrate using the format() function, issue the following:
    print( 'College is Great'.format() )
  3. The above example does not actualy do any formatting, next add a string using the format() function arguments
    print('College is {}'.format('Great'))
    When the format function reads {} curly braces, it performs special actions. For example, if format() finds {} with nothing inside it substitutes the string from it's arguments. These arguments are done by position left to right

  4. To demonstrate, issue the following:
    print('{} {} {}'.format('a', 'b', 'c'))
    However, using this method while quick, easy, and clean has a issue. If more curly braces {} are in the string than in the format() arguments, it will create an error.

  5. To demonstrate by creating too many braces, issue the following:
    print('{} {} {} {}'.format('a', 'b', 'c'))
    For situations like above, if reusing strings more than once is important, positional values can be placed inside the curly braces.

  6. Issue the following to see what happens:
    print('{0} {1} {1} {2}'.format('a', 'b', 'c'))
    print('{2} {2} {1} {0}'.format('a', 'b', 'c'))
    These positions make formating each string much less prone to errors while also making the string being formatted much more clear.

  7. To improve on formatting further, issue the following to provide the format() function with string variables:
    course_name = 'Open System Automation'
    course_code = 'OPS435'
    print('{0} {1} {0}'.format(course_code, course_name))
    The format() function by default tries to use values as strings, all values is {} are displayed as strings unless extra options are given.

  8. Issue the following:
    course_number = 435             # This is an integer
    print('This is displaying a string by default: {0}'.format(course_number))
    Next, let's place a list inside the format() function and access the values, there are two ways to use the list.

  9. Let's demonstrate a range of alternative methods of printing a list by issuing the following:
    list1 = [1,2,3,4,5]                                            # This is a list of numbers
    print('{0[0]} {0[1]} {0[2]} {0[3]} {0[4]}'.format(list1))      # Access the values of the list via {position[index]} 
    print('{0} {1} {2} {3} {4}'.format(*list1))                    # Expand the list into multiple positional arguments
    print('{} {} {} {} {}'.format(*list1))                         # Expand the list into multiple positional arguments
    print('{0} {1} {2} {3} {4}'.format(1,2,3,4,5))                 # Same results as above
    Let's now place a dictionary inside the format() functions and access the values, again there are a few ways to use the dictionary

  10. Issue the following:
    dict_york = {'Address': '70 The Pond Rd', 'City': 'Toronto', 'Country': 'Canada', 'Postal Code': 'M3J3M6', 'Province': 'ON'}
    print('{}'.format(dict_york))                                  # Print entire dictionary
    print('{0}'.format(dict_york))                                 # Print entire dictionary using format arguments position
    print('{0[City]} {0[Country]}'.format(dict_york))              # Print values using position and key {position[key]}
    With dictionaries however, instead of using positional arguments 0 each access to a key, python allows for expansion of keyword arguments.

  11. Let's take a look at the example of a keyword arguments, place the keyword variable name in between {} and add the keyword argument to the format() function:
    print('{string1} is {string2} {string2}'.format(string1='College', string2='Great!'))
  12. Variables may also be passed to these keyword arguments:
    college = 'Seneca College'
    print('{string1} is {string2} {string2}'.format(string1=college, string2='Great!'))
    Now back to dictionaries. Using keyword arguments(sometimes referred to as kwargs in python). The dictionary can be quickly and easily expanded into these keyword arguments using the syntax **dictionary_name.

  13. Let's try this below. Pay close attention to the keys inside the dictionary and the values associated with each key:
    # Define a dictionary:
    dict_york = {'Address': '70 The Pond Rd', 'City': 'Toronto', 'Country': 'Canada', 'Postal Code': 'M3J3M6', 'Province': 'ON'}
    # Uses the dictionary's keyword arguments:
    print('{City} {Province} {Country}'.format(**dict_york))                                        
    # Creates new keyword arguments:
    print('{City} {Province} {Country}'.format(City='Toronto', Province='ON', Country='Canada'))

PART 2 - String Formatting Advanced Features

This section shows you how to perform more advanced formatting features via the format() function. You will learn how to format numbers and aligning strings (text).

Perform the Following Steps
  1. Make certain you are still in your ipython3 shell.

    Let's start advanced formatting for numbers. Try issuing content below in order to observe the various ways to change the output inside the curly brace {} while formatting. This formatting change is done by placing a colon inside the curly braces, following by a letter {0:f}

  2. Issue the following:
    number1 = 50 
    number2 = -50
    number3 = 1.50
    print('{0:f}'.format(number1))          # "0" is the position just like other format() functions
    print('{0:f}'.format(number2))          # A colon separate the position/index areas from the extra functionality
    print('{0:f}'.format(number3))          # "f" represents the fixed point number
    Notice that there are many decimal places after this number. This makes the number look "ugly". We need to further format the number to indicate the number of decimal places (or no decimal places if number is an integer). A fixed point number means that it can control the number of digits that come after the decimal point, try changing the .2 to any other number and experiment.

  3. Issue the following to demonstrate:
    print('{0:.0f}'.format(number1))        # Show no digits after decimal point
    print('{0:.2f}'.format(number2))        # Show two digits after decimal point
    print('{0:.1f}'.format(number3))        # Show one digit after decimal point
  4. Numbers can be displayed with the - or + signs before the digits, this could be important in formatting. Issue the following to demonstrate:
    print('{0: f}'.format(number1))                     # Show a space where plus sign should be
    print('{0: f}'.format(number2))                     # Shows negative sign normally
    print('{0: f}\n{1: f}'.format(number1, number2))    # The space before the f lines up positive and negative numbers
  5. Placing a + before the f changes the format so that plus signs show up for positive numbers and negative signs show up for negative numbers. Try issuing the following:
    print('{0:+f}'.format(number1))                     # Show a space where plus sign should be
    print('{0:+f}'.format(number2))                     # Shows negative sign normally
    print('{0:+f}\n{1:+f}'.format(number1, number2))    # The space before the f lines up positive and negative numbers
  6. Combining fixed point positions can be performed by issuing the following:
    print('{0:+.2f}'.format(number1))
    print('{0:+.2f}'.format(number2))
    print('{0:+.2f}\n{1:+.2f}'.format(number1, number2))
    In the event that the numbers being shown are all integers and do no require the decimal values, instead of using {:f},
    use {:d} for decimal integers.

  7. To demonstrate, issue the following:
    print('{0:d}'.format(number1))
    print('{0:d}'.format(number2))
    print('{0: d}'.format(number1))
    print('{0: d}'.format(number2))
    print('{0:+d}'.format(number1))
    print('{0:+d}'.format(number2))
    NOTE: When using {:d} be careful not to use numbers with a decimal value in them, the following will create a error

  8. Issue the following to see the difference:
    number3 = 1.50
    print('{0:d}'.format(number3))
    Next, let's move on to aligning strings (text). This is the process of adding padding to the left of our text, the right of our text, or aligning to the centre or our text (i.e. padding both left and right). Through the alignment the text field size, any string can be set to allow it to fit within a column (or columns), and make it easier for the user to read data.

  9. Start by using a string but placeing a number value after the colon {0:10} by issuing:
    string1 = 'hello'
    string2 = 'world'
    print('{0:10}{1}'.format(string1, string2))      # Make sure string1 is 10 characters aligned to the left
    print('{0:6}{1}'.format(string1, string2))       # Make sure string1 is 6 characters aligned to the left
    By default, the format() function aligns to the left, the symbol to do this is < which is a shortcut for : {0:<10} when used in the curely braces. Now whenever a different string is placed inside it always aligns to the left 10 characters. This allows for concise code to generate well structured output

  10. To demonstrate this, issue the following:
    # Without positional argument numbers
    print('{:<10} {:<10} {:<10}\n{:<10} {:<10} {:<10}'.format('abc', 'def', 'ghi', 'jkl123', 'mno123', 'pqr123'))
    # With positional argument numbers
    print('{0:<10} {1:<10} {2:<10}\n{3:<10} {4:<10} {5:<10}'.format('abc', 'def', 'ghi', 'jkl123', 'mno123', 'pqr123'))
  11. Next try using right alignment with the > symbol replacing the left alignment
    # Without positional argument numbers
    print('{:>10} {:>10} {:>10}\n{:>10} {:>10} {:>10}'.format('abc', 'def', 'ghi', 'jkl123', 'mno123', 'pqr123'))
    # With positional argument numbers
    print('{0:>10} {1:>10} {2:>10}\n{3:>10} {4:>10} {5:>10}'.format('abc', 'def', 'ghi', 'jkl123', 'mno123', 'pqr123'))
  12. Finally, you can centre the alignment of strings using the ^ symbol. Issue the following:
    # Without positional argument numbers
    print('{:^10} {:^10} {:^10}\n{:^10} {:^10} {:^10}'.format('abc', 'def', 'ghi', 'jkl123', 'mno123', 'pqr123'))
    # With positional argument numbers
    print('{0:^10} {1:^10} {2:^10}\n{3:^10} {4:^10} {5:^10}'.format('abc', 'def', 'ghi', 'jkl123', 'mno123', 'pqr123'))
    The alignment character can be changed to any other character that is wanted for the output. By default it's a space, but it could be anything else by adding an additional character {:M<10} such as M or * before the alignment character

  13. To see this, issue the following:
    print('{:*^10} {:*^10} {:*^10}\n{:*^10} {:*^10} {:*^10}'.format('abc', 'def', 'ghi', 'jkl123', 'mno123', 'pqr123'))       # Without positional argument numbers
    print('{:.^10} {:.^10} {:.^10}\n{:.^10} {:.^10} {:.^10}'.format('abc', 'def', 'ghi', 'jkl123', 'mno123', 'pqr123'))       # Without positional argument numbers
    You can make the output appear better by creating borders. Below is a function in order to create a border containing data. If you learned MySQL, you may have seen borders used to display table data, etc. Try defining and running the user-defined function below to see what happens.

  14. Issue the following:
    # Define the function "print_with_borders()":
    def print_with_borders():
        print('|{:-^10}|'.format('nums'))
        print('|{:^5}{:^5}|'.format(1,2))
        print('|{:^5}{:^5}|'.format(3,4))
        print('|{}|'.format('-'*10))
    
    # Run the function "print_with_borders()":
    print_with_borders()

Create a Script Demonstrating Formatting Strings

Perform the Following Instructions:
  1. Create the ~/ops435/lab4/lab4e.py script. The purpose of this script is to demonstrate formatting string output from a large data structure.
  2. Use the following template to get started:
    #!/usr/bin/env python3
    # Formatted Strings
    
    dict_york = {'Address': '70 The Pond Rd', 'City': 'Toronto', 'Country': 'Canada', 'Postal Code': 'M3J3M6', 'Province': 'ON'}
    college = 'Seneca College'
    
    def print_college_address():
        # Prints out the keys and values from the dictionary
        # Formats the output to have a title bar with a title
        # EXACT SAME output as the samples
    
    if __name__ == '__main__':
        print_college_address(dict_york, college)
  • The print_college_address() function does NOT return anything
  • The print_college_address() function accept two arguments
  • The first argument is a dictionary
  • The second argument is a string
  • The title printed is center aligned by 40 characters using '-' instead of space for alignment
  • The keys printed are center aligned by 20 characters
  • The values printed are center aligned by 20 characters
  • The output must match the sample output EXACTLY if one character is off it will be wrong
Sample Run 1:
run lab4e.py
|-------------Seneca College-------------|
|      Address          70 The Pond Rd   |
|      Province               ON         |
|    Postal Code            M3J3M6       |
|        City              Toronto       |
|      Country              Canada       |
|----------------------------------------|
Sample Run 2(with import):
import lab4e
dict_york = {'Address': '70 The Pond Rd', 'City': 'Toronto', 'Country': 'Canada', 'Postal Code': 'M3J3M6', 'Province': 'ON'}
dict_newnham = {'Address': '1750 Finch Ave E', 'City': 'Toronto', 'Country': 'Canada', 'Postal Code': 'M2J2X5', 'Province': 'ON'}
college = 'Seneca College'

lab4e.print_college_address(dict_york, college)
|-------------Seneca College-------------|
|      Address          70 The Pond Rd   |
|      Province               ON         |
|    Postal Code            M3J3M6       |
|        City              Toronto       |
|      Country              Canada       |
|----------------------------------------|

lab4e.print_college_address(dict_newnham, college)
|-------------Seneca College-------------|
|      Address         1750 Finch Ave E  |
|      Province               ON         |
|    Postal Code            M2J2X5       |
|        City              Toronto       |
|      Country              Canada       |
|----------------------------------------|
3. Exit the ipython3 shell, download the checking script and check your work. Enter the following commands from the bash shell.
cd ~/ops435/lab4/
pwd #confirm that you are in the right directory
ls CheckLab4.py || wget matrix.senecac.on.ca/~acoatley-willis/CheckLab4Extras.py
python3 ./CheckLab4.py -f -v lab4e
4. Before proceeding, make certain that you identify any and all errors in lab4e.py. When the checking script tells you everything is OK before proceeding to the next step.