Module 5
Module 5
Syllabus:
Output Formatting:
Python’s str.format() method of the string class allows you to do variable substitutions and value
formatting. This lets you concatenate elements together within a string through positional
formatting.
This tutorial will guide you through some of the common uses of formatters in Python, which
can help make your code and program more readable and user friendly.
Using Formatters
Formatters work by putting in one or more replacement fields or placeholders — defined by a
pair of curly braces {} — into a string and calling the str.format() method. You’ll pass into the
method the value you want to concatenate with the string. This value will be passed through in
the same place that your placeholder is positioned when you run the program.
In the example above, we constructed a string with a pair of curly braces as a placeholder:
The values that exist within the method look like this:
They are essentially the tuple data type and each individual value contained in the tuple can be
called by its index number, which starts with the index number 0.
We can pass these index numbers into the curly braces that serve as the placeholders in the
original string:
Output:
But, if we reverse the index numbers with the parameters of the placeholders we can reverse the
values being passed into the string:
Specifying Type:
We can include more parameters within the curly braces of our syntax. We’ll use the format code
syntax {field_name:conversion}, where field_name specifies the index number of the argument
to the str.format() method that we went through in the reordering section, and conversion refers
to the conversion code of the data type that you’re using with the formatter.
The conversion type refers to the the single-character type code that Python uses. The codes that
we’ll be using here are s for string, d to display decimal integers (10-base), and f which we’ll use
to display floats with decimal places. You can read more about the Format-Specification Mini-
Language through Python 3’s official documentation.
Let’s look at an example where we have an integer passed through the method, but want to
display it as a float by adding the f conversion type argument:
We used the syntax of {field_name:conversion} for the first curly brace replacement field to
output a float. The second curly braces only uses the first parameter {field_name}.
In the example above, there are a lot of numbers displaying after the decimal point, but you can
limit those. When you are specifying f for float values, you can additionally specify the precision
of that value by including a full stop . followed by the number of digits after the decimal you
would like to include.
If Sammy ate 75.765367% of the pizza, but we don’t need to have a high level of accuracy, we
can limit the places after the decimal to 3 by adding .3 before the conversion type f:
We can add a number to indicate field size (in terms of characters) after the colon : in the curly
braces of our syntax:
Output:
Sammy has 5 red balloons !
Now we see that 5 is left-aligned, providing space in the field before red, and balloons is
centered in its field with space to the left and right of it.
By default, when we make a field larger with formatters, Python will fill the field with
whitespace characters. We can modify that to be a different character by specifying the character
we want it to be directly following the colon:
print("{:*^20s}".format("Sammy"))
Output
*******Sammy********
We are accepting the string being passed to str.format() in the index position of 0 since we did
not specify otherwise, including the colon, and specifying that we will use * instead of space to
fill up the field. We're centering the string with ^, specifying that the field is 20 characters in
size, and also indicating that we are working with a string conversion type by including s.
We can combine these parameters with other parameters we’ve used before:
Output:
x is 1
Following is another example where we import names and marks of students from a list and print
them using template.
# A Python program to demonstrate working of string template
from string import Template
for i in Student:
print (t.substitute(name = i[0], marks = i[1]))
Output:
Hi Ram, you have got 90 marks
Hi Ankit, you have got 78 marks
Hi Bob, you have got 92 marks
The substitute() method raises a KeyError when a placeholder is not supplied in a dictionary or a
keyword argument. For mail-merge style applications, user supplied data may be incomplete and
the safe_substitute() method may be more appropriate — it will leave placeholders unchanged if
data is missing:
Another application for template is separating program logic from the details of multiple output
formats. This makes it possible to substitute custom templates for XML files, plain text reports,
and HTML web reports.
Multithreading
Multiple threads can exist within one process where:
Each thread contains its own register set and local variables (stored in stack).
All thread of a process share global variables (stored in heap) and the program code.
Consider the diagram below to understand how multiple threads exist in memory:
Multithreading is defined as the ability of a processor to execute multiple threads concurrently.
Multithreading in Python
In Python, the threading module provides a very simple and intuitive API for spawning multiple
threads in a program.
Let us consider a simple example using threading module:
# Python program to illustrate the concept
# of threading
# importing the threading module
import threading
def print_cube(num):
"""
function to print cube of given num
"""
print("Cube: {}".format(num * num * num))
def print_square(num):
"""
function to print square of given num
"""
print("Square: {}".format(num * num))
if __name__ == "__main__":
# creating thread
t1 = threading.Thread(target=print_square, args=(10,))
t2 = threading.Thread(target=print_cube, args=(10,))
# starting thread 1
t1.start()
# starting thread 2
t2.start()
Output:
Square: 100
Cube: 1000
Done!
To create a new thread, we create an object of Thread class. It takes following arguments:
target: the function to be executed by thread
args: the arguments to be passed to the target function
In above example, we created 2 threads with different target functions:
t1 = threading.Thread(target=print_square, args=(10,))
t2 = threading.Thread(target=print_cube, args=(10,))
To start a thread, we use start method of Thread class.
t1.start()
t2.start()
Once the threads start, the current program (you can think of it like a main thread) also
keeps on executing. In order to stop execution of current program until a thread is
complete, we use join method.
t1.join()
t2.join()
As a result, the current program will first wait for the completion of t1 and then t2. Once,
they are finished, the remaining statements of current program are executed.
Consider the diagram below for a better understanding of how above program works:
Consider the python program given below in which we print thread name and corresponding
process for each task:
# Python program to illustrate the concept
# of threading
import threading
import os
def task1():
print("Task 1 assigned to thread: {}".format(threading.current_thread().name))
print("ID of process running task 1: {}".format(os.getpid()))
def task2():
print("Task 2 assigned to thread: {}".format(threading.current_thread().name))
print("ID of process running task 2: {}".format(os.getpid()))
if __name__ == "__main__":
# print ID of current process
print("ID of process running main program: {}".format(os.getpid()))
# creating threads
t1 = threading.Thread(target=task1, name='t1')
t2 = threading.Thread(target=task2, name='t2')
# starting threads
t1.start()
t2.start()
As it is clear from the output, the process ID remains same for all threads.
We use threading.main_thread() function to get the main thread object. In normal
conditions, the main thread is the thread from which the Python interpreter was
started. name attribute of thread object is used to get the name of thread.
print("Main thread name: {}".format(threading.main_thread().name))
Logging in Python:
Logging is a means of tracking events that happen when some software runs. Logging is
important for software developing, debugging and running. If you don’t have any logging record
and your program crashes, there are very little chances that you detect the cause of the problem.
And if you detect the cause, it will consume a lot of time. With logging, you can leave a trail of
breadcrumbs so that if something goes wrong, we can determine the cause of the problem.
There are a number of situations like if you are expecting an integer, you have been given a float
and you can a cloud API, the service is down for maintenance and much more. Such problems
are out of control and are hard to determine
Python has a built-in module logging which allows writing status messages to a file or any other
output streams. The file can contain the information on which part of the code is executed and
what problems have been arisen.
Levels of Log Message
There are two built-in levels of the log message.
Debug : These are used to give Detailed information, typically of interest only when
diagnosing problems.
Info : These are used to Confirm that things are working as expected
Warning : These are used an indication that something unexpected happened, or indicative
of some problem in the near future
Error : This tells that due to a more serious problem, the software has not been able to
perform some function
Critical : This tells serious error, indicating that the program itself may be unable to
continue running
If required, developers have the option to create more levels but these are sufficient enough to
handle every possible situation. Each built-in level has been assigned its numeric value.
Logging module is packed with several features. It has several constants, classes, and methods.
The items with all caps are constant, the capitalize items are classes and the items which start
with lowercase letters are methods.
There are several logger objects offered by the module itself.
Logger.info(msg) : This will log a message with level INFO on this logger.
Logger.warning(msg) : This will log a message with level WARNING on this logger.
Logger.error(msg) : This will log a message with level ERROR on this logger.
Logger.critical(msg) : This will log a message with level CRITICAL on this logger.
Logger.log(lvl,msg) : This will Logs a message with integer level lvl on this logger.
Logger.
exception(msg) : This will log a message with level ERROR on this logger.
The Basics
Basics of using the logging module to record the events in a file are very simple.
For that, simply import the module from the library.
1. Create and configure the logger. It can have several parameters. But importantly, pass the
name of the file in which you want to record the events.
2. Here the format of the logger can also be set. By default, the file works in append mode
but we can change that to write mode if required.
3. Also, the level of the logger can be set which acts as the threshold for tracking based on the
numeric values assigned to each level.
There are several attributes which can be passed as parameters.
4. The list of all those parameters is given in Python Library. The user can choose the
required attribute according to the requirement.
After that, create an object and use the various methods as shown in the example.
#importing module
import logging
#Creating an object
logger=logging.getLogger()
#Test messages
logger.debug("Harmless debug Message")
logger.info("Just an information")
logger.warning("Its a Warning")
logger.error("Did you try to divide by zero")
logger.critical("Internet is down")
Output:
The above code will generate a file with the provided name and if we open the file, the file
contains the following data.