Learning Python by Example ACE INTL Nodrm
Learning Python by Example ACE INTL Nodrm
Learner’s Guide
No part of this book may be reproduced or copied in any form or by any means – graphic, electronic or
mechanical, including photocopying, recording, taping, or storing in information retrieval system or sent or
transferred without the prior written permission of copyright owner Aptech Limited.
APTECH LIMITED
Contact E-mail: ov-support@onlinevarsity.com
In the dynamic realm of programming education, mastering Python serves as a foundational skill
for aspiring developers. "Python-Learning by Example" is a comprehensive guide designed to take
beginners through the intricacies of Python programming with practical examples. It begins with a
gentle introduction to Python, laying the foundation with a thorough exploration of data types,
operators, and flow control statements. As readers progress, they delve into functions, parameters,
and essential data structures like lists, tuples, dictionaries, and sets, fostering a solid understanding
of Python's core concepts.
Moving beyond the basics, the book navigates through iterators, generators, decorators, and
Object-Oriented Programming (OOP) principles. It further delves into advanced OOP techniques
and exception handling strategies, equipping learners with the skills to write robust and error-
tolerant code. Additionally, the book explores serialization, file handling, threading,
networking, and database interactions, empowering readers to apply Python in a variety of real-
world scenarios and harness its full potential as a versatile programming language.
This book is the result of a concentrated effort of the Design Team, which is continuously striving to
bring you the best and the latest in Information Technology. The process of design has been a part
of the ISO 9001 certification for Aptech-IT Division, Education Support Services. As part of Aptech’s
quality drive, this team does intensive research and curriculum enrichment to keep it in line
with industry trends.
Design Team
Table of
Contents
Sessions
1. Introduction to Python
Appendix
SESSION 01
INTRODUCTION TO PYTHON
Learning Objectives
In this session, students will learn to:
Python, known for its simplicity and readability, is a highly popular high-level
programming language widely used in diverse domains such as Web
development, data analysis, scientific computing, and Artificial Intelligence. The
online Python community actively contributes to its continuous improvement
since it is an open-source language with freely available source code.
In the late 1980s, the foundations for the history of Python were being laid, with
the commencement of work on the programming language. It was during this
time that its development was initiated, led by Guido Van Rossum at Centrum
Wiskunde and Informatica (CWI) in the Netherlands. The application-based work
on Python began in December 1989.
On February 20, 1991, the first version of Python was released. Interestingly, the
name of the Python programming language was inspired by an old BBC television
© Aptech Limited
comedy sketch series called Monty Python's Flying Circus. It was rather than the
large snake commonly associated with the name.
Python, with its simplicity, readability, and flexibility, is a versatile and widely-used
programming language.
Open Source
Python is an open-source language, which means its source code is freely available for anyone to
use, modify, and distribute.
Cross-Platform
Python programs can be run on various operating systems, including Windows, macOS, Linux, and
others, without the necessity for any modifications.
© Aptech Limited
Third-Party Libraries
Python's potential is greatly enhanced across multiple domains. This includes data analysis, Machine
Learning, Web development, and scientific computing, by an extensive collection of third-party libraries
and packages such as NumPy, Pandas, TensorFlow, Django, Flask, and others.
Dynamic Typing
Python's dynamic typing feature allows the type of a variable to be determined dynamically at runtime.
This eliminates the necessity of an explicit declaration of variable types, thereby contributing its ease of use
and adaptability.
Object-Oriented
Python supports Object-Oriented Programming (OOP) principles, allowing creation and working with
classes and objects.
High-level Language
Python abstracts many low-level details, making the code more human-readable and reducing its
complexity.
Productivity and Speed: Python's concise syntax boosts productivity by enabling efficient, readable code
with fewer lines, which increases productivity.
Community and Support: Developers are provided ample documentation, tutorials, and support by Python's
big and active community.
Flexibility: Python's versatility makes it a powerful language for diverse applications, as it can be used in a
wide variety of domains.
Integration: Python easily integrates with other languages such as C, C++, and Java, enabling developers to
leverage existing codebases.
Scalability: Python's performance can be further enhanced by utilizing libraries such as NumPy or Cython for
computationally intensive tasks.
Career Opportunities: Python's widespread adoption has created high demand for programmers, making it
a top choice for technical career opportunities.
© Aptech Limited
1.3.3 Usage of Python
Several popular IDEs for Python developments are widely used and highly
regarded in the Python community, each offering its own set of features and
capabilities.
Visual Studio Code (VS Code) is a lightweight yet potent code editor with an
extensive extension library. When equipped with the right extensions, it is favored
by programmers for its speed and customizability, offering a comprehensive
Python development experience.
Spyder, explicitly designed for scientific computing with Python, offers a MATLAB-
such as interface, a variable explorer, an IPython console, and a powerful code
editor.
© Aptech Limited
Sublime Text, while not Python-specific, remains popular among Python
programmers as a highly customizable and lightweight code editor.
Ultimately, the best IDE for Python depends on the programmer's individual
requirements, comfort level with the interface, and specific project requirements.
In the ‘Downloads’ section, the ‘Latest Python X.XX’ button (replace ‘X.XX’ with
the latest version number) can be clicked.
The appropriate Windows installer for the system, usually ‘Windows installer (64-
bit)’ for most modern PCs, is selected at the bottom of the page.
During the installation process, ensure that the box ‘Add Python X.X to PATH’ is
checked.
The installation wizard will guide the user and Python will be installed on the
Windows machine.
© Aptech Limited
Command Prompt, PowerShell, and Bash on Windows Subsystem for Linux,
offering users a seamless experience across different operating systems and shell
environments.
If Homebrew is not already installed, following command can be pasted into the
Terminal to install it:
The free Community edition or the Professional edition (if there is a license) of
PyCharm can be downloaded, depending on the user's requirements.
After the download is complete, the installer should be run and the on-screen
instructions should be followed.
Running PyCharm:
After PyCharm is installed, it can be run from the Start menu on Windows or from
the Applications folder on macOS.
Configuring Python Interpreter in PyCharm:
When PyCharm is opened for the first time, the user will be asked to configure the
Python interpreter. The Python version installed earlier (example: Python X.X)
should be chosen as the interpreter in PyCharm.
© Aptech Limited
System Interpreter is chosen and the Python executable installed earlier is
browsed and selected.
Now, Python and PyCharm are set up on the Windows or macOS system, and
Python coding can be done using the PyCharm IDE.
In Python, blocks of code are defined using indentation instead of curly braces,
as is done in many other programming languages. Consistent indentation is
considered essential for Python code to work correctly. Typically, four spaces are
used for indentation, though tabs can also be used (although mixing spaces and
tabs is not recommended).
Code Snippet 1:
# This is a comment
print("Hello, World!") # This is another comment
In the Code Snippet 1, Python print statement is used to output the string Hello,
World! to the console.
1.6.2 Variables
Data and values are stored in Python using variables, which are assigned values
through the utilization of the assignment operator (=).
© Aptech Limited
Code Snippet 2 shows an example with variables of different types.
Code Snippet 2:
x = 10 # integer
y = 3.14 # floating-point number
is_student = True # Boolean
In Code Snippet 2, the codeinitializes three variables in Python with different data
types. Variable x holds an integer value of 10, y stores a floating-point number
3.14, and is_student is set to the Boolean value True. Comments alongside each
variable indicate their respective data types for clarity and documentation.
Code Snippet 3 shows how to write data types through several examples.
Code Snippet 3:
In Code Snippet 3, the codedefines variables number, pi, name, and is_student,
assigning them values of 42 (an integer), 3.14 (a floating-point number), "Alice" (a
string), and True (a boolean), respectively. These variables store different data
types—integer, floating-point, string, and boolean—representing numerical
values, text, and logical states.
1.6.4 Operators
Python includes various operators to perform operations on data, such as
arithmetic, comparison, logical, assignment, and more.
© Aptech Limited
Code Snippet 4:
In Code Snippet 4, the code use of arithmetic and comparison operators. Initially,
two variables, a and b, are assigned values of 10 and 5, respectively. The addition
operator `+` combines these values to yield 15, stored in addition_result. The
comparison operator `>` evaluates whether a is greater than b, resulting in
is_greater being assigned True. This concise example illustrates the
fundamental concepts of arithmetic operations and value comparisons in
Python.
Code Snippet 5:
In Code Snippet 5, the code checks the value of the variable age. If it's less than
18, it prints You are a minor. If it's between 18 and 54 (inclusive), it prints You
are an adult. Otherwise, it prints You are a senior citizen.
1.6.6 Loops
A block of code is repeatedly executed through the utilization of loops. Python
supports for and while loops.
© Aptech Limited
Code Snippet 6:
In Code Snippet 6, the code consists of two parts. The first part utilizes a for loop
to iterate over the range from 0 to 4 inclusive, printing each number in the
sequence. The second part demonstrates a similar iteration using a while loop. It
initializes a count variable to 0, and within the loop, it prints the current count and
increments it until the count reaches 5.
1.6.7 Functions
Blocks of reusable code that perform specific tasks are represented by functions.
They aid in code organization and the facilitation of reusability.
Code Snippet 7:
© Aptech Limited
result = add(5, 7)
print(result)
# Output: 12
In Code Snippet 7, the code defines a function named add that takes two
parameters and returns their sum. Then, it calls this function with arguments 5 and
7, storing the result in the variable result, which is subsequently printed, resulting
in the output 12.
Code Snippet 8:
Code Snippet 9:
name = input("Enter your name: ")
© Aptech Limited
print("Hello,", name)
In Code Snippet 8, the codeprompts the user to enter their name using the
input() function and stores the input in the variable name. Then, it prints a
greeting message using the entered name by concatenating it with the string
"Hello," using the print() function.
variable
my_var
© Aptech Limited
Count
total_sum
_name
MAX_VALUE
__private_var
this_is_a_long_identifier
1variable
123Identifier
@invalid_identifier
my-variable
this_identifier!
if
for
while
def
class
Having spaces in the name
invalid identifier
my variable
It is important to note that using invalid identifiers will result in syntax errors or
unexpected behavior in your Python code. The rules for defining valid identifiers
should always be followed to maintain code correctness and readability.
© Aptech Limited
1.8 Keywords in Python
Keywords in Python are reserved words with specific meanings and purposes
within the language. As part of the Python syntax, they cannot be used as
variable names or identifiers. Some of the commonly used keywords in Python are
mentioned in Table 1.1.
Keyword Definition
FALSE Representing the boolean value False.
None Representing a null or empty value.
TRUE Representing the boolean value True.
and Serving as the logical AND operator.
as Used in import statements or for creating aliases for
modules and classes.
assert Utilized for checking if a condition is true, raising an
error if the condition is false.
async Employed to define asynchronous functions.
await Used for pausing execution within an async function
until an awaited coroutine completes.
break Used for prematurely exiting from a loop.
class Used to define a class.
continue Used to proceed to the next iteration of a loop.
def Used to define a function.
del Employed to delete variables or items from a list or
dictionary.
elif An abbreviation of ‘else if’, used in conditional
statements.
else Used in conditional statements as a fallback when the
condition is not met.
except Used in exception handling to define a block of code
that runs when an exception occurs.
finally Used in exception handling to define a block of code
that always runs, regardless of whether an exception
occurred.
for Used to create loops.
from Used in import statements to import specific parts of a
module.
global Used to indicate that a variable inside a function
should be treated as a global variable.
if Used to define conditional statements.
© Aptech Limited
Keyword Definition
import Used to import modules or packages.
in Used in for loops to iterate over elements in a
sequence.
is Used to test object identity.
lambda Used to create anonymous functions (also known as
lambda functions).
nonlocal Used in nested functions to indicate that a variable
should be treated as a non-local variable.
not The logical NOT operator.
or The logical OR operator.
pass Used as a placeholder for empty blocks of code.
raise Used to raise exceptions.
return Used to return a value from a function.
try Used to start exception handling blocks.
while Used to create loops.
with Used to simplify exception handling and resource
management with context managers.
yield Used in generators to produce a value.
© Aptech Limited
1.9 Summary
Python was created in the early 1990s by Guido van Rossum and has a rich
history, boasting a large community of users and programmers.
PyCharm, Visual Studio Code, and Jupyter Notebook are popular IDEs for
Python, with PyCharm often being preferred by many programmers.
Identifiers in Python are names used for variables, functions, and so on, and
must adhere to certain naming rules.
Python keywords are reserved words with specific meanings and cannot
be utilized as identifiers.
© Aptech Limited
1.10 Test Your Knowledge
1. What is Python Programming Language?
2.Which of the following statements about Python's uses and features is correct?
A) Python is primarily used for Web development and cannot be used for other
purposes
B) Python is a statically typed language, meaning data types must be declared
explicitly before using variables
C) Python has a strong emphasis on readability and uses indentation for code
blocks instead of braces or keywords
D) Python is not an open-source language, and its usage requires a commercial
license
3. Python is widely used in the field of data analysis and scientific computing
due to its extensive libraries and tools.
A) True
B) False
5. Which of the following is the correct syntax to print ‘Hello, World!’ in Python?
A) print("Hello, World!")
B) print("Hello, World!")
C) Print("Hello, World!")
D) PRINT("Hello, World!")
© Aptech Limited
6. Which of the following is a valid identifier in Python?
A) 123Python
B) _myVar
C) @variable
D) while
© Aptech Limited
1.10.1 Answers to Test Your Knowledge
1. A high-level programming language
2. Python has a strong emphasis on readability and uses indentation for
code blocks instead of braces or keywords
3. True
4. PyCharm
5. print("Hello, World!")
6. _myVar
7. Keywords are special words reserved by Python, representing predefined
actions or meanings
© Aptech Limited
Try It Yourself
2. Write a Python program that asks the user to enter two numbers and then,
prints the sum of those two numbers.
© Aptech Limited
SESSION 02
DATA TYPES AND OPERATORS IN PYTHON
Learning Objectives
In this session, students will learn to:
Data types in Python define the kinds of values variables can hold. Python is a
dynamically typed language, so explicit specification of data types during
variable declaration is not required. The data type is automatically assigned
based on the value given to the variable.
© Aptech Limited
Here are some common sequence types in Python:
List:
String Type:
Displays an ordered collection of
Shows a collection of characters
items, which can be of different data
contained within single or double
types and can be modified
quotation marks. For example, ‘hello’,
(mutable). For example, [1, 'apple',
'world'.
3.14].
Mapping Type/Dictionary:
Frozenset:
Represents a collection of key-value
Represents an immutable set. It is
pairs. Each key must be unique and
similar to sets but cannot be modified
mapped to a value. For example,
after creation. For example,
{'name': 'John', 'age': 30}.
frozenset({1, 2, 3}).
None Type:
Null value or "None" in Python represents the absence of a value. It is
commonly used to indicate that a variable lacks any valid data.
In Python, numeric types represent numerical values. Python supports two main
numeric data types:
Integer
Integers are numeric data types that represent whole numbers, positive or
negative without any fractional part.
You can assign an integer value to a variable using the standard variable
assignment syntax.
© Aptech Limited
Code Snippet 1:
age = 25
grade = 85
In Code Snippet 1, the snippet defines two variables: age and grade. The variable
age is assigned a value of 25, indicating the age of an individual, while grade is
assigned a value of 85.
Float
Float data type represents floating-point numbers, which are numbers that have
a fractional part. The numbers can have a positive, negative, or zero value. Floats
are used to represent real numbers and can be used for various purposes, such
as mathematical calculations and representing decimal values.
Code Snippet 2:
Height = 6.1
Weight = 50.22
Pi = 3.1416
In addition to int and float, Python supports several other data types that serve
different purposes. The data types frequently utilized in Python include:
Complex Types
A complex type is used to represent complex numbers, which are numbers with
both a real and an imaginary part. Complex numbers are denoted as a + bj,
where a is the real part, b is the imaginary part, and j represents the square root
of -1.
To create a complex number in Python, you can use the complex() function or
simply express the number directly using the j suffix for the imaginary part.
© Aptech Limited
Code Snippet 3 shows the usage of a complex() function.
Code Snippet 3:
In Code Snippet 3, the code creates two complex numbers using the `complex`
function. `z1` is initialized with a real part of 2 and an imaginary part of 3, resulting
in the complex number `2 + 3j`. `z2` is initialized with a real part of 0 and an
imaginary part of -4, leading to the complex number `0 - 4j`.
Code Snippet 4:
z1 = 2 + 3j
z2 = 4 - 1j
# Addition
result_add = z1 + z2 #Output:6 + 2j
# Subtraction
result_sub = z1 - z2 #Output:-2 + 4j
# Multiplication
result_mul = z1 * z2 #Output:11 + 10j
# Division
result_div = z1 / z2 #Output:0.4 + 0.7j
© Aptech Limited
In Code Snippet 4, the code performs arithmetic operations on two complex
numbers, z1 (2 + 3j) and z2 (4 - 1j). It calculates their addition, subtraction,
multiplication, and division, storing the outcomes in result_add(6 + 2j),
result_sub(-2 + 4j), result_mul (11 + 10j), and result_div (0.4 + 0.7j),
respectively.
10 0.0 3.14j
Binary Types
The byte data type is a built-in data type in Python that is used to work with binary
data. It is often combined with other data types such as strings. It can hold values
from 0 to 255 and represents a group of 8 binary digits. Binary data, such as
images or audio files and low-level network protocols are commonly manipulated
using bytes.
The byte array data type, also present in Python, is a mutable sequence of bytes
used for handling binary data. It is commonly combined with other data types,
such as strings, to manipulate binary data. Unlike bytes, byte arrays can be
modified in place, providing a more flexible approach to binary data
manipulation.
Hexadecimal Type
In Python, hexadecimal is a base-16 numbering system utilized for representing
numbers, especially when binary data and low-level programming tasks are
involved. Hexadecimal numbers utilize digits ranging from 0 to 9 and letters from
© Aptech Limited
A to F. In this system, A signifies 10, B represents 11, C stands for 12, and so forth,
up to F, which denotes 15.
Code Snippet 5 shows the representation of Hexadecimal data type.
Code Snippet 5:
print(hex_value) # Output: 26
Figure 2.1depicts the output of this code when executed through PyCharm.
2.4 Boolean
The Boolean data type in Python is a fundamental type representing two values:
True and False. It is crucial for managing conditional statements and logical
operations in Python programming. The program flow is controlled based on the
truthiness or falseness of expressions, which are evaluated using Boolean values.
It is important to note that the keywords ’True‘and ’False‘must always be
capitalized.
© Aptech Limited
Code Snippet 6:
# Boolean operations
bool_result = True and False
# Outcome of this will be False
In Code Snippet 6, the code assigns Boolean values to variables and uses if-else
statements for weather-related outputs. It also demonstrates Boolean logic,
resulting in specific true or false outcomes through logical operations.
Figure 2.2 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 2.2: Output of Code Snippet 6
Code Snippet 7:
x = 10
y = 20
In Code Snippet 7, the code compares integers `x` and `y` using relational
operators, assigning the results to variables. `is_greater` is `False` because `x` is
not greater than `y`, `is_equal` is `False` as `x` does not equal `y`, and
`is_less_than` is `True` indicating `x` is less than `y`.
Type conversion functions in Python are utilized for converting variables or data
from one data type to another. With these functions, the data representation can
be altered to enable specific operations or assignments.
The commonly used type conversion functions in Python are listed as follows.
Code Snippet 8:
num_str = "10"
num_int = int(num_str)
© Aptech Limited
print(num_int)
# Output: 10
In Code Snippet 8, the code converts a string `"10"` to an integer using the `int()`
function and prints the integer value `10`.
Code Snippet 9:
float_str = "3.14"
float_num = float(float_str)
print(float_num)
# Output: 3.14
In Code Snippet 9,The code converts the string `"3.14"` to a floating-point number
using the `float()` function and prints the number `3.14`.
number = 42
string_num = str(number)
print(string_num)
# Output: "42"
In Code Snippet 10,The code converts the integer `42` to a string using the `str()`
function and prints the string `"42"`.
© Aptech Limited
Code Snippet 11:
num_tuple = (1, 2, 3)
num_list = list(num_tuple)
print(num_list)
# Output: [1, 2, 3]
In Code Snippet 11, the code converts a tuple containing the integers `1`, `2`,
and `3` into a list with the same elements using the `list()` function and prints
the list `[1, 2, 3]`.
value = 0
bool_value = bool(value)
print(bool_value)
# Output: False
In Code Snippet 12, the code converts the integer `0` to a boolean using the
`bool()` function and prints the result. Since `0` is typically interpreted as `False`
in boolean context in Python, `bool_value` is set to `False`, which is then printed.
In Python, the None type is used to represent the absence of a value or a null
value. This built-in constant frequently used to indicate that a variable or function
does not hold a reference to any valid object. The None object is a singleton,
meaning there is only one instance of it in memory.
© Aptech Limited
The None type is represented by the keyword None. It is not equated with an
empty string, zero, or any other false value. None is a distinct object representing
‘nothing’.
my_variable = None
In Code Snippet 13, the code assigns the value `None` to the variable
`my_variable`. `None` is a special value in Python used to denote the absence
of a value or a null reference.
The None type represents the absence of a value or a null value in Python. It is
utilized to signify that a variable does not point to any object. The None object is
a singleton and is often used as a default return value for functions that do not
return anything explicitly.
def greet():
print("Hello, there!")
result = greet() # Output: "Hello, there!"
print(result) # Output: None
In Code Snippet 14,The code defines a function `greet()` that prints "Hello,
there!" when called. After calling `greet()`, the text "Hello, there!" is printed.
The function does not explicitly return a value, so `result` is assigned `None`.
Printing `result` outputs `None`.
None is often used as a default value for function arguments when you want to
allow the caller to omit the argument and use a predefined value.
© Aptech Limited
Code Snippet 15:
In Code Snippet 15, the code defines a function named multiply that takes two
parameters, a and an optional b with a default value of None. If b is not provided,
it defaults to 1. The function returns the product of a and b. When multiply is
called with a single argument, 5, it returns 5, as b defaults to 1. Calling multiply
with 5 and 3 returns 15, demonstrating the function's ability to handle both
provided and default arguments.
When comparing to None, use is instead of ==, as is checks for identity (same
object in memory), while == checks for equality.
a = None
b = None
In Code Snippet 16, the code checks if variables `a` and `b`, both initialized with
the value `None`, are indeed `None` and compares them to each other. It prints
`True` for all checks, confirming that `a` is `None`, `a` equals `None`, and `a`
and `b` refer to the same `None` object in memory.
© Aptech Limited
Code Snippet 17:
Result = None
In Code Snippet 17, the variable `Result` is assigned the value `None`, indicating
it has no value assigned.
2.6.2 String
Strings, sequences of characters enclosed in single ('') or double ("") quotes, can
contain letters, numbers, symbols, and spaces. Once created, strings cannot be
modified directly due to their immutability.
In Code Snippet 18, the code assigns the string "Hello, World!" to the variable
`my_string`.
2.6.3 Lists
Lists are collections of items that are ordered and mutable. Elements of different
data types can be contained within a list and it is possible to add, modify, or
remove elements from it. Lists are formed by placing elements inside square
brackets ([]).
© Aptech Limited
Code Snippet 19:
# Define a list
numbers = [1, 2, 3, 4, 5]
In Code Snippet 19, the Code Snippet defines a list named `numbers` containing
the integers 1 through 5. It then prints this initial list. Afterward, a new number, 6, is
added to the list using the `append` method.
Figure 2.3 depicts the output of this code when executed through PyCharm.
2.6.4 Tuple
Tuples are collections of items that are ordered and immutable, similar to lists, but
their elements cannot be changed once created. Tuples are defined using
parentheses (()).
my_tuple = (1, 2, 3, 4, 5)
© Aptech Limited
# Try to modify the tuple (which should raise an
error)
try:
my_tuple[0] = 10 # Attempting to modify the first
element of the tuple
except TypeError as e:
print("Error occurred:", e)
else:
print("Tuple modified successfully")
In Code Snippet 20, The code attempts to modify the first element of a tuple
named `my_tuple`, which contains the integers 1 through 5. Tuples in Python are
immutable, meaning their elements cannot be changed once assigned. The
attempt to modify the tuple is wrapped in a try-except block to catch the
`TypeError` that occurs when trying to modify it. Upon catching the error, the
code prints an error message. If no error occurred (which is not possible in this
scenario), it would print "Tuple modified successfully."
Figure 2.4 depicts the output of this code when executed through PyCharm.
2.6.5 Dictionaries
Dictionaries consist of unordered key-value pairs. Each key is unique and used to
access its corresponding value. Dictionaries are defined using curly braces ({})
and are useful for fast lookups.
© Aptech Limited
In Code Snippet 21 theperson dictionary contains information about an
individual, structured with key-value pairs. Each key represents a specific attribute
such as name, age, and occupation, while the corresponding values provide the
associated details, such as John for the name, 30 for the age, and "Engineer" for
the occupation. This dictionary allows easy access to different aspects of the
person's profile, facilitating organized storage and retrieval of information.
Figure 2.5 depicts the output of this code when executed through PyCharm.
2.6.6 Sets
Sets are collections of unique elements that are unordered. Duplicate values are
not allowed in sets. Sets can be defined using curly braces ({}) or the set()
constructor.
In Code Snippet 22, the code defines a list named `numbers_list` containing
integers, some of which are repeated. It then creates a set named
`unique_numbers_set` from this list, effectively removing any duplicates due to
the nature of sets, which only store unique elements. Finally, it prints the set,
displaying the unique numbers from the original list.
Figure 2.6 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 2.6: Output of Code Snippet 22
© Aptech Limited
Code Snippet 23 shows how to represent Escape Chars.
In Code Snippet 23, demonstrates the use of escape characters in Python to add
new lines, tab spaces, backslashes, single quotes, and double quotes within
strings. This technique allows for more complex string formatting.
Figure 2.7 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Escape characters enable creation of strings with special formatting and the
inclusion of challenging characters that may be hard to represent directly.
2.8 Constants
In Python, constants are a special type of variable whose value cannot be
changed. Within a module, constants are typically declared and assigned.
The module itself comprises variables, functions, and other code elements in a
separate file. This allows the constants to be imported into the main file for use.
Although Python lacks built-in support for constants, programmers conventionally
employ uppercase variable names to suggest that the variable should be treated
as a constant. However, it's important to note that the value of such variables can
still technically be changed.
In the program given in Code Snippet 24a, it will define constants for the freezing
and boiling points of water in both Celsius and Fahrenheit scales. Then, it will
create a function to convert between the two scales.
# Constants
FREEZING_C = 0
BOILING_C = 100
FREEZING_F = 32
BOILING_F = 212
# Usage
celsius_temp = 25
fahrenheit_temp = celsius_to_fahrenheit(celsius_temp)
© Aptech Limited
print(f"{celsius_temp} degrees Celsius is
{fahrenheit_temp} degrees Fahrenheit")
fahrenheit_temp = 98.6
celsius_temp = fahrenheit_to_celsius(fahrenheit_temp)
print(f"{fahrenheit_temp} degrees Fahrenheit is
{celsius_temp} degrees Celsius")
In Code Snippet 24a, defines constants for the freezing and boiling points of water
in Celsius and Fahrenheit and includes functions to convert between these
temperature scales, illustrating the use of constants in Python.
Figure 2.8 depicts the output of this code when executed through PyCharm.
In the program given in Code Snippet 24b, it will define a constant named PI
based on mathematical constant Pi. It is then used PI to calculate the area and
circumference of a circle.
# Constant.py
PI = 3.14159
# Usage
radius = 5
© Aptech Limited
area = calculate_circle_area(radius)
circumference =
calculate_circle_circumference(radius)
Figure 2.9 depicts the output of this code when executed through PyCharm.
2.9 Operators
Operators in Python are symbols or special characters used for performing
specific operations on variables or values. A variety of operators are supported
by Python, which can be broadly categorized into following types:
© Aptech Limited
iii. Multiplication: *
iv. Division: /
v. Modulus (remainder): %
vi. Exponentiation: **
vii. Floor Division (integer division): //
Code Snippet 25 shows how Arithmetic Operators can be used in Python with
these variables.
# Arithmetic Operators
a = 10
b = 3
print(a + b) # Output: 13
print(a / b) # Output: 3.3333333333333335
print(a % b) # Output: 1
Equal to: ==
Not equal to: !=
Greater than: >
Less than: <
Greater than or equal to: >=
Less than or equal to: <=
© Aptech Limited
print(a <= b) # Output: False
In Code Snippet 26, comparison operators are used to evaluate the relationship
between two variables, illustrating how to compare values for equality, greater
than, and less than or equal conditions.
c = 5
c += 2 # Equivalent to c = c + 2
print(c) # Output: 7
x = True
y = False
© Aptech Limited
print(x and y) # Output: False
print(x or y) # Output: True
print(not x) # Output: False
In Code Snippet 28, logical operators are demonstrated here, performing logical
AND and OR operations on Boolean values, showcasing conditional logic in
Python.
2.9.5 Bitwise Operators
Operators that perform bitwise operations on integer values are listed as follows:
i. Bitwise AND: &
ii. Bitwise OR: |
iii. Bitwise XOR: ^
iv. Bitwise NOT: ~
v. Left shift: <<
vi. Right shift: >>
# Bitwise OR (|)
result = operand1 | operand2
© Aptech Limited
In Code Snippet 28, this snippet exemplifies bitwise operations, including AND, OR,
XOR, NOT, left shift, and right shift, demonstrating how to manipulate individual
bits within integer values in Python.
Figure 2.10 depicts the output of this code when executed through PyCharm.
Membership (in): in
When using the in operator in Python, if the specified value is present in the
sequence (such as a string, list, or tuple), the result will be True; if it is not present,
the result will be False.
When using the not in operator, if the value is absent in the sequence, the result
will be True, and if it is present, the result will be False.
© Aptech Limited
2.9.7 Identity Operators
These operators compare the memory location of two objects.
Identity equality (is): is
Identity inequality (is not): is not
p = [1, 2, 3]
q = [1, 2, 3]
print(p is q)
# Output: False (Different objects in memory)
print(p is not q)# Output: True
In Code Snippet 31, the code compares two lists to check if they reference the
same object in memory, illustrating the use of identity operators is and is not
for comparison, showing that even identical lists are distinct objects.
Single-line comments in Python are initiated with the hash symbol (#) and
continue until the end of the line. Anything appearing after the # symbol on the
same line will be treated as a comment and will not be executed by the Python
interpreter.
© Aptech Limited
In Code Snippet 32, demonstrates how to use single-line comments in Python,
allowing for annotations and explanations within the code without affecting the
program's execution, showcased by printing a greeting message.
2.10.2 Multi-line comments (Docstrings)
In Python, the usage of triple quotes (''' or """) is permitted to generate multi-line
comments, known as docstrings. Although docstrings are primarily utilized for
documenting functions, classes, or modules they can also be employed as
multiline comments.
"""
This is a multi-line comment in Python.
You can use it to add detailed explanations or notes
about your code.
"""
# Your actual code starts here
def my_function():
"""
This is a docstring for the function.
It provides information about what the function does.
"""
print("Hello, world!")
# Calling the function
my_function()
In Python, sequence types are data types that represent ordered collections of
elements, where each element is assigned, a unique index starting from 0. These
elements can be of any data type, including integers, strings, tuples, lists, and so
on. Common characteristics are shared by sequence types in Python, and they
© Aptech Limited
support specific operations that make them useful in various programming
scenarios.
2.11.1 Strings
Strings are sequences of characters and are immutable, meaning once they are
created, their contents cannot be changed. You can access individual
characters using indexing and slicing.
In Code Snippet 34, this snippet creates a range object representing numbers
from 0 to 4 and converts it to a list for printing, demonstrating how to generate
sequences of numbers in Python.
2.11.2 Range
In Code Snippet 35, the first part of the code demonstrates the creation of a
range object representing numbers from 0 to 4 and then converting this range
into a list for display purposes.
These types are used to represent sequences of bytes. Bytes are immutable,
while bytearrays are mutable.
Code Snippet 36 shows how bytes and bytearrays are used in Python.
© Aptech Limited
Code Snippet 36:
my_bytes = b"Hello"
print(my_bytes[1]) # Output: 101 (the ASCII code for
'e')
my_bytearray = bytearray(b"Hello")
my_bytearray[0] = 72 # Modify the first byte to
represent 'H'
print(my_bytearray)
# Output: bytearray(b'Hello')
In Code Snippet 36, the code shows accessing an element from a bytes object,
`my_bytes`, to print the ASCII code for 'e'. It then modifies a `bytearray`
object, `my_bytearray`, to demonstrate its mutability by changing its first
element to 'H', showcasing how bytes are immutable but bytearrays can be
altered.
Lists, Tuples, and Strings are also categorized as ‘homogeneous’ sequence types
since, they can only hold elements of the same type (example, a list of integers,
a tuple of strings).
© Aptech Limited
2.12 Summary
Complex numbers, binary, and hexadecimal types can also be used for
specific calculations and representations.
Boolean values, True and False are utilized for logical operations.
Special types such as None are used to represent the absence of a value.
Sequence types such as lists, tuples, and strings are fundamental data
structures in Python, used to store collections of elements in an ordered
manner.
© Aptech Limited
2.13 Test Your Knowledge
© Aptech Limited
7. Which of the following statements regarding constants in Python is true?
A) Constants in Python are defined using the 'const' keyword
B) Constants in Python can be changed after their initial assignment
C) Python does not support constants; all variables can be modified
D) Constants in Python are usually written in uppercase letters and cannot be
reassigned
8. The '==' operator in Python is used for both assignment and comparison of
values.
A) True
B) False
10. Which of the following statements about Sequence Types in Python is true?
A) Sequence types can only contain elements of the same data type
B) Lists are immutable sequence types
C) Strings are mutable sequence types
D) Sequence types preserve the order of elements
© Aptech Limited
2.13.1 Answers to Test Your Knowledge
1. Complex
2. int()
3. logical
4. A missing or undefined value
5. Tuple
6. All of these
7. Constants in Python are usually written in uppercase letters and cannot be
reassigned.
8. False
9. False
10. Sequence types preserve the order of elements.
© Aptech Limited
Try It Yourself
© Aptech Limited
SESSION 03
FLOW CONTROL STATEMENTS IN PYTHON
Learning Objectives
In this session, students will learn to:
Control flow statements, such as if, elif, and else, allow you to
Conditional execute specific blocks of code only if certain conditions are met.
Execution This enables your program to make decisions and choose different
actions based on different scenarios.
Loops, such as while and for loops, are crucial for performing
Repetition repetitive tasks efficiently. They help you avoid writing redundant
and Loops code by executing a set of instructions multiple times, often with
varying inputs or conditions.
© Aptech Limited
Properly structured control flow makes your code more
Code
organized, readable, and maintainable. It helps separate
Organization
different functionalities and keeps related code blocks together.
Sequential Execution
Sequential execution pertains to the inherent order of statement execution,
progressing one after another as they appear in the code.
Each statement is executed in sequence, and the program flows from the top to
the bottom of the code without any branching or decision-making. This type of
execution is straightforward and is suitable for tasks that involve a linear series of
steps.
Code Snippet 1 shows sequential execution in Python. Here all the statements are
executed in a straight sequence.
Code Snippet 1:
print("Step 1")
print("Step 2")
print("Step 3")
Conditional Execution
Conditional execution involves making decisions based on conditions and
executing different code blocks depending on whether those conditions are true
or false.
© Aptech Limited
Conditional execution uses control flow statements such asif, elif (else if), and
else to direct the program execution path.
The code inside each conditional block is executed only if the associated
condition evaluates to true.
Code Snippet 2:
temperature = 25
if temperature > 30:
print("It is hot!")
elif temperature > 20:
print("It is warm.")
else:
print("It is cool.")
#Output: It is warm.
3.2Conditional Statements
Conditional statements in Python are an essential feature that allows you to make
decisions in your code based on certain conditions. These statements enable your
program to execute different blocks of code depending on whether a given
condition is true or false. The primary conditional statements in Python are if, elif
(else if), and else.
3.2.1 if Statement
The simplest form of a conditional statement is the if statement. It allows you to
execute a block of code only if a specific condition is true.
Syntax of the if statement in Python:
if condition:
# execute the code if the condition is true
© Aptech Limited
Code Snippet 3:
x = 10
if x > 5:
print("x is greater than 5")
if condition:
# executethe code if the condition is true
else:
# execute the code if the condition is false
Code Snippet 4:
if-elif-else Statement
The if-elif-else statement allows you to check multiple conditions in sequence
and execute the corresponding block of code for the first true condition
encountered. If no conditions are true, the else block is executed (if provided).
© Aptech Limited
Syntax of if-elif-else statement in Python:
If condition1:
# execute the code if condition1 is true
elif condition2:
# execute the code if condition2 is true
else:
# execute the code if neither condition1 nor #
# condition2 is true
Code Snippet 5 shows an example of if-elif-else statement in Python.
Code Snippet 5:
Figure 3.1 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Nested if Statements
You can also nest conditional statements within each other to create more
complex decision-making structures.
Code Snippet 6 shows how a nested if statement is used.
Code Snippet 6:
x = 10
if x > 0:
if x % 2 == 0:
print("x is a positive even number")
else:
print("x is a positive odd number")
else:
print("x is not a positive number")
3.3Loops
Loops in Python are control structures that allow you to repeatedly execute a block
of code. They are essential for performing repetitive tasks, iterating over
sequences, and automating actions. Python supports two main types of loops: the
for loop and the while loop.
Code Snippet 7:
© Aptech Limited
for fruit in fruits:
print(fruit) # apple, banana, cherry
While condition:
# code to execute while condition is true
Code Snippet 8:
count = 0
while count < 5:
print(count)
count += 1
#Output: 0
1
2
3
4
To simulate switch-like behavior using dictionaries in Python, you can use the
dictionary to map keys (cases) to corresponding values (actions or functions). A
step-by-step example is explained as follows:
Code Snippet 9 shows how to achieve switch-like behavior using dictionaries.
Code Snippet 9:
def get_letter_grade(grade):
grade_switch = {
© Aptech Limited
90: "A",
80: "B",
70: "C",
60: "D"
}
return "F"
© Aptech Limited
return x * y
operator_functions = {
'+': add,
'-': subtract,
'*': multiply,
'/': divide
}
Figure 3.2 depicts the output of this code when executed through PyCharm.
Loop control statements in Python allow you to control the flow of loops by altering
their execution based on certain conditions. The main loop control statements are
break, continue, and the optional else clause for loops.
© Aptech Limited
break
The break statement allows an early exit from a loop, even when the loop
condition remains valid. Upon encountering a break, the loop instantly concludes,
and the program proceeds to the subsequent statement following the loop.
for i in range(5):
if i == 3:
break# Breaks out of loop when i is 3
print(i)
#Output: 0
1
2
Continue
for i in range(5):
if i == 2:
continue # Skips printing when i is 2
print(i)
# Output: 0
1
2
3
4
© Aptech Limited
Loop else Clause
Python allows you to use an optional else clause with loops, which is executed
only if the loop completes normally (that is, not terminated by a break statement).
This can be used for a special action at the end of a loop or to check for conditions
that were not met during iteration.
Code Snippet 13 shows an example of using else statement in a loop.
for i in range(5):
print(i)
else:
print("Loop completed without break.")
#Output: 0
1
2
3
4
Loop completed without break
Pass
Although not a loop control statement, the pass statement is a placeholder that
does nothing. It is used when syntactically a statement is required, but the
developer does not want any code to be executed. Leaving the code empty
would cause an error to be raised because empty code is not allowed in loops,
function definitions, class definitions, or in if statements. In such a case, pass is
useful.
for i in range(3):
pass # Placeholder for future code
# No output is generated because pass statement does
nothing
© Aptech Limited
3.6 Iterating with enumerate() and zip()
enumerate() and zip() are two powerful functions in Python that enhance ability
to iterate over sequences and collections. They provide convenient ways to work
with data and perform various operations during iteration.
3.6.1 enumerate() Function
Code Snippet 15
#Output:
Item 1: apple
Item 2: banana
Item 3: cherry
© Aptech Limited
Code Snippet 17:
numbers = [1, 2, 3]
letters = ['A', 'B', 'C']
for num, letter in zip(numbers, letters):
print(num, letter)
#Output:
1 A
2 B
3 C
Code Snippet 18 shows if the input iterables are of different lengths, zip() stops
generating tuples when the shortest iterable is exhausted.
numbers = [1, 2, 3]
letters = ['A', 'B']
for num, letter in zip(numbers, letters):
print(num, letter)
#Output:
1 A
2 B
You can combine enumerate() and zip() to iterate over two or more sequences
while tracking their indices.
Code Snippet 19 shows the combination of enumerate and zip functions
together.
© Aptech Limited
for index, (name, age) in enumerate(zip(names,
ages)):
print(f"Person {index + 1}: {name}, Age: {age}")
#Output:
Person 1: Alice, Age: 25
Person 2: Bob, Age: 30
Person 3: Charlie, Age: 22
List comprehensions are a concise and efficient way to create lists in Python. They
provide a compact syntax for generating lists by applying an expression to each
item in an iterable(such as a range, a list, or a string) and collecting the
results.
List comprehensions can make code more readable and reduce the necessity for
explicit loops.
Code Snippet 20 shows the basic structure of a list comprehension.
Syntax:
List Comprehensions can include conditions to filter items before they are added
to the new list as follows:
© Aptech Limited
Code Snippet 20:
#Output:
(‘red’, ‘apple’)
(‘red’, ‘grape’)
(‘red’, ‘berry’)
(‘green’, ‘apple’)
(‘green’, ‘grape’)
(‘green’, ‘berry’)
(‘blue’, ‘apple’)
(‘blue’, ‘grape’)
(‘blue’, ‘berry’)
Error handling using try and except blocks in Python is a fundamental concept
that allows you to handle and manage exceptions (errors) that occurs during the
execution of your code. By using these blocks, you can write code that gracefully
handles errors, provides useful feedback, and prevents crashes.
© Aptech Limited
Code Snippet 22 shows an example of usingtry and exceptin Python.
Code Snippet 22:
Try:
# Code that might raise an exception
result = 10 / 0 # Division by zero will raise a
# ZeroDivisionError
except ZeroDivisionError:
print("Cannot divide by zero.")
except ValueError as e:
print(f"ValueError: {e}")
#output: Cannot divide by zero.
You can have multiple except blocks to catch different types of exceptions.
The except blocks specify the type of exception to catch (for example,
ZeroDivisionError, ValueError).
You can use the as keyword to assign the exception object to a variable for further
examination or printing.
3.8.1 Display Custom Error Messages
In the except block, print a custom error message that explains the nature of the
error and provides context to the user or developer.
© Aptech Limited
Code Snippet 23 shows the error message along with an explanation.
try:
value = int("abc")
except ValueError:
print("Error: Invalid value provided. Please enter a
valid integer.")
© Aptech Limited
3.9 Summary
The break statement ends the current loop or iteration, while continueskips
the rest of the current iteration and moves to the next one.
© Aptech Limited
3.10 Test Your Knowledge
2. Which of the following statements is true regarding if, elif, and else
statements in Python?
A) An elsestatement can be used without an if statement
B) elif stands for end if
C) An if statement can have only one corresponding elifstatement
D) else statements are used to handle multiple conditions within the same
block
© Aptech Limited
4. Which statement accurately describes the key difference between a
whileloop and a for loop in Python?
A) A while loop is used for iterating over sequences, while a forloop is used to
execute a block of code repeatedly as long as a condition is met
B) A for loop is ideal for indefinite loops, while a while loop is suitable for
iterating over a sequence of elements
C) In a for loop, the number of iterations is determined by a condition, whereas
a while loop iterates over a fixed range of values
D) A while loop can only iterate over lists, while a for loop can work with various
data structures
© Aptech Limited
3.10.1 Answers to Test Your Knowledge
1. True
2.An else statement can be used without an ifstatement
3.True
4. Afor loop is ideal for indefinite loops, while a while loop is suitable for iterating
over a sequence of elements
5.The loop terminates immediately and control passes to the next iteration
6.They are used to handle specific types of exceptions and provide alternative
code paths when errors occur
© Aptech Limited
Try It Yourself
© Aptech Limited
Learning Objectives
In this session, students will learn to:
♦ Define the basics of functions in Python
♦ Explain the types of arguments and concept of parameters
♦ Identify the user-defined functions in Python
♦ Explain lambda functions and callables in Python
4.1 Introduction to Functions in Python
def function_name(parameters):
# Function body
# Perform tasks here
return result # Optional
Function Definition: It starts with the def keyword, followed by the function
name. Function names should be descriptive and follow the lowercase with
underscores (snake_case) naming convention. Parentheses ()are used to
enclose the parameters that the function accepts.
© Aptech Limited
Parameters: Parameters are placeholders for the values that the function
expects to receive when it is called. They provide a way to pass information
into the function. Parameters are listed within the parentheses after the
function name.
Function Body: The indented block of code inside the function specifies the
function's behavior. It contains the logic and operations that are
performed when the function is called.
Built-in Functions: These are functions that are pre-defined in Python and are
readily available for use without any additional imports.
Examples include print(),len(),max(),min(),and str().
© Aptech Limited
In Code Snippet 1, you have a simple function named greet() that, when
called, prints the message “Welcome to the course on Python”.
4.2 Arguments
Arguments in Python are values that are passed to a function when it is called,
enabling the function to work with specific data. They provide a way to
customize a function behavior and process different inputs such as numeric
values, strings, and lists. Functions typically require input data to perform their
tasks, and arguments fulfill this requirement.
Default Argument
In Python, default values can be assigned to the parameters of a function.
These default values are used if the caller of the function does not provide a
value for that parameter. This concept is known as using default arguments.
© Aptech Limited
Syntax of default arguments in Python:
def function_name(parameter1=default_value1,
parameter2=default_value2, ...):
# Function code goes here
Code Snippet 3:
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
© Aptech Limited
Code Snippet 4 shows an example that demonstrates the use of keyword
arguments.
Code Snippet 4:
def describe_person(name, age, city):
print(f"{name} is {age} years old and lives in {city}.")
# Output:
# Alice is 30 years old and lives in New York.
# Bob is 25 years old and lives in Los Angeles.
Positional Arguments
In Python functions, positional arguments are the simplest form of arguments.
They are passed to a function in the order they appear in the function
parameter list. The argument's position signifies its correspondence with a
specific parameter.
Code Snippet 5:
def describe_person(name, age, city):
print(f"{name} is {age} years old and lives in {city}.")
© Aptech Limited
describe_person("Alice", 30, "New York")
Code Snippet 6:
def print_arguments(*args):
for arg in args:
print(arg)
Figure 4.1 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 4.1: Output of Code Snippet 6
Code Snippet 7:
def print_keyword_arguments(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
© Aptech Limited
Using both *args and **kwargs
You can use both *args and **kwargs in the same function definition to accept
both positional and keyword arguments.
Code Snippet 8 shows an example of using both *args and **kwargs.
Code Snippet 8:
def combined_arguments(arg1, arg2, *args, kwarg1=None,
**kwargs):
print("Fixed arguments:", arg1, arg2)
print("Additional positional arguments:", args)
print("Keyword arguments:", kwargs)
© Aptech Limited
There are two main types of parameters in Python functions:
Positional Parameters
These are the most common type of parameters. They are defined in the
function declaration and their values are assigned based on the order in which
the arguments are passed when the function is called.
Syntax of positional parameters in Python:
def example_function(parameter1, parameter2):
# Function code here
Keyword Parameters
Keyword parameters (also known as named parameters) are specified using
the parameter names as keywords when calling the function. This allows you to
pass values to specific parameters out of order.
Functions are flexible and can have any quantity of parameters, including zero.
Additionally, default values can be employed for parameters, rendering them
as optional when the function is called. If a default value is provided, the
parameter becomes a keyword parameter, and it can be omitted when
calling the function.
Code Snippet 9 shows the usage of greeting parameters in Python.
Code Snippet 9:
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
© Aptech Limited
In this example, the greeting parameter has a default value of Hello, making it
optional when calling the function. If no value is provided for greeting, the
default value is used.
User-defined functions in Python are blocks of reusable code that you create
to perform specific tasks. These functions are defined by the developer to
encapsulate a sequence of operations into a single named unit. They enhance
code organization, readability, and reusability.
01
def:
This keyword is used to declare the start of a function definition.
function_name:
02 Choose a meaningful name for your function. It should follow
the same naming conventions as variable names.
Parameters:
03
These are input values that the function accepts. They are
optional, but if present, they are enclosed within parentheses.
Multiple parameters are separated by commas.
Function Body:
04 This is where you write the code that performs the intended
task.
© Aptech Limited
return Statement:
result = calculate_square(5)
print(result)
# Output: 25
© Aptech Limited
In Code Snippet 11, a lambda function for squaring a number is shown,
highlighting the concise syntax for writing anonymous functions that perform
simple operations in a single line.
Lambda functions are commonly used when you require a quick function for a
short operation, such as sorting, filtering, or mapping. They are often used as
arguments to higher-order functions such as map, filter, and sorted.
For instance, using a lambda function with the sorted function to sort a list of
tuples based on the second element.
Code Snippet 12 shows an example of a lambda function to sort a list of tuples
based on the second element.
In Code Snippet 12, it sorts a list of tuples, `points`, based on the second
element of each tuple. The `sorted` function is used with a `lambda` function
as the key, which specifies that the sorting should be done according to the
second item (`point[1]`) in each tuple. The sorted list is stored in
`sorted_points` and then printed, resulting in the list being sorted in
ascending order by the second element of the tuples.
Lambda functions are limited in scope and are best suited for simple
operations. For more complex functions, it is advisable to use regular named
functions defined using the def keyword.
In Python, the term callable refers to objects that can be called as functions.
It encompasses various entities, including functions, methods, classes, and
objects with a __call__ method defined. Essentially, any object that can be
invoked using parentheses, such as a function call, is considered callable.
© Aptech Limited
Main categories of callables in Python are follows:
01 Regular functions created using the def keyword are callable. They
can be defined with parameters and a body of code to execute.
Functions
03
Lamda
Lambda functions, also known as anonymous functions, are short,
one-liner functions defined using the lambda keyword. They are
callable as well.
Function
Function
def my_function(x):
return x + 1
result = my_function(3) # Calling the function
Method
class MyClass:
def my_method(self, x):
return x + 1
obj = MyClass()
result = obj.my_method(3) # Calling the method
© Aptech Limited
Lambda Function
square = lambda x: x ** 2
result = square(5) # Calling the lambda function
obj = CallableClass()
result = obj(3) # Calling the object as if it is a function
callable_obj = CustomCallable()
result = callable_obj(3) # Calling the callable object
In addition to the basic types of functions, other important types are as follows:
Generator Functions
These functions use the yield keyword to produce values one at a time,
allowing for memory-efficient iteration over large data sets or infinite
sequences.
Code Snippet 13 shows how to define a generator function in Python.
© Aptech Limited
In Code Snippet 13, a generator function named `my_generator` is illustrated,
using `yield` to produce a sequence of values. This example introduces the
concept of generator functions for efficient iteration over sequences.
Figure 4.4 depicts the output of this code when executed through PyCharm.
Decorator Functions
Decorators modify or enhance the behavior of other functions or methods.
They are applied using the @ symbol and are used for tasks such as logging,
timing, and access control.
Code Snippet 14 shows how to define a decorator function in Python.
@my_decorator
def my_function(x, y):
return x + y
result = my_function(5, 3)
print(result)
© Aptech Limited
Figure 4.5 depicts the output of this code.
Higher-Order Functions
These are functions that take one or more functions as arguments or return
functions as results. They are essential for functional programming techniques.
Recursive Functions
Recursive functions call themselves to solve problems that can be broken down
into smaller instances of the same problem. Examples include the calculation
of factorial or fibonacci numbers.
© Aptech Limited
return 1
# Recursive case
else:
return n * factorial(n - 1)
result = factorial(5)
print(result)
# Output: 120
Closures
A closure is a nested function that captures and remembers the values in its
enclosing function scope even after the outer function has finished executing.
Partial Functions
Partial functions allow the setting of specific arguments for a function, resulting
in the creation of a new function with those values already predefined. This is
useful for creating specialized functions from more general ones. It is created
using funtools.partial.
Code Snippet 16 shows an example to create partial function in Python.
# Original function
def power(x, y):
return x ** y
In Code Snippet 16, partial functions are created from a base `power` function
using `functools.partial`, illustrating how to pre-specify some arguments
of a function to create a new function.
© Aptech Limited
Async Functions
Used for asynchronous programming, these functions allow tasks to run
concurrently without waiting for each to complete before moving on to the
next task.
Code Snippet 17 shows an example to define and use async function in
Python.
Figure 4.6 depicts the output of this code when executed through PyCharm.
Namespace Functions
Functions associated with namespaces, such as globals() and locals(),
provide access to the dictionaries of the global or local namespace.
© Aptech Limited
Error Handling Functions
Functions such as try, except, and finally are used for handling exceptions
and ensuring proper cleanup in case of errors.
Code Snippet 18 shows an example of error handling function in Python.
except ZeroDivisionError:
print("Error: Division by zero")
return None
except TypeError:
print("Error: Invalid data type")
return None
except Exception as e:
print("An unexpected error occurred:", e)
return None
else:
return result
numerator = 10
# Test cases
denominators = [2, 0, "abc"]
for denominator in denominators:
result = divide(numerator, denominator)
if result is not None:
print(f"Result of {numerator} / {denominator} =
{result}")
© Aptech Limited
Figure 4.7: Output of Code Snippet 18
In this example, the divide function attempts to perform division and handles
various exceptions using the try, except, and else blocks:
i. If a ZeroDivisionError occurs, it prints an error message.
ii. If a TypeError occurs (due to non-numeric input), it prints an error
message.
iii. If any other exception occurs, it prints a generic error message.
iv. If no exception occurs, the division result is returned.
© Aptech Limited
In Code Snippet 19, a function to calculate the area of a rectangle is shown,
demonstrating basic arithmetic operations and interaction with user input.
Figure 4.8 depicts the output of this code when executed through PyCharm.
© Aptech Limited
# Call the function with keyword parameters
volume_keyword = calculate_volume(length=length,
width=width, height=height)
© Aptech Limited
4.7 Summary
Functions in Python are reusable blocks of code defined using the def
keyword.
In Python, arguments are values passed to functions when they are called.
© Aptech Limited
4.8 Test Your Knowledge
© Aptech Limited
5. Which of the following is true about default parameter values?
A) They are not allowed in Python functions
B) They must be assigned to a value outside the function
C) They provide values to parameters if no argument is provided
D) They are used only for complex data types
© Aptech Limited
4.8.1 Answers to Test Your Knowledge
2. def
© Aptech Limited
Try It Yourself
4. Write a Python program on a function that takes a list of strings and returns
a new list with all strings capitalized.
© Aptech Limited
Learning Objectives
In this session, students will learn to:
♦ Outline the concept of List in Python
♦ Explain the creation and working of Lists in Python
♦ Describe the process of receiving input and adding elements
♦ Define the process of concatenating and slicing Tuples in Python
5.1 Introduction to List in Python
In Python, a list is a commonly used data structure that allows you to store a
collection of items. These items can be of any data type, including integers,
strings, floating-point numbers, other lists, and even more complex objects. Lists
are mutable, which means you can modify their contents by adding, removing,
or updating elements after they are created.
Code Snippet 1:
In Code Snippet 1, various types of lists in Python are created, demonstrating the
creation of lists with integers, strings, mixed data types, and an empty list. This
snippet showcases the versatility of Python lists in storing different types of data.
Figure 5.1 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 5.1: Output of Code Snippet 1
Code Snippet 2:
def main():
# Using square brackets
list1 = [1, 2, 3, 4, 5]
© Aptech Limited
# Using list slicing
original_list = [1, 2, 3, 4, 5]
sliced_list = original_list[1:4]
if __name__ == "__main__":
main()
In Code Snippet 2, different methods to create and manipulate lists are explored,
including using the list constructor, list comprehension, the append and extend
methods, repetition operator, slicing, and converting a string to a list. This snippet
provides a comprehensive overview of list operations in Python.
Figure 5.2 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 5.2: Output of Code Snippet 2
Code Snippet 3:
my_list = [10, 20, 30, 40, 50]
© Aptech Limited
sliced_elements = my_list[1:4] # Accesses elements # from
index 1 to 3 ([20, 30, 40])
print(first_element)
print(second_element)
print(last_element)
print(second_to_last)
print(sliced_elements)
In Code Snippet 3, accessing elements from a list using positive and negative
indices, along with slicing to retrieve a range of elements, is demonstrated. This
illustrates how to access and manipulate list items.
Figure 5.3 depicts the output of this code when executed through PyCharm.
Code Snippet 4:
© Aptech Limited
last_element = my_list[-1]
# Accesses the last element (50)
second_to_last = my_list[-2]
# Accesses the second-to-last element (40)
third_from_end = my_list[-3]
# Accesses the third-to-last element (30)
print(last_element)
print(second_to_last)
print(third_from_end)
In Code Snippet 4, negative indexing to access elements from the end of a list is
shown. This method allows easy retrieval of elements without needing the list's
length.
Figure 5.4 depicts the output of this code when executed through PyCharm.
Code Snippet 5:
my_list = [10, 20, 30, 40, 50]
list_size = len(my_list)
© Aptech Limited
print("Size of the list:", list_size)
# Output: Size of the list: 5
In Code Snippet 5, the len() function is used to get the size of a list, highlighting
how to determine the number of elements in a list.
Code Snippet 6:
# Taking input from the user as a comma-separated #string
input_string = input("Enter a number list separated by
commas:")
In Code Snippet 6, input from a user is taken and converted into a list of integers,
demonstrating dynamic list creation based on user input.
Figure 5.5 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Three most commonly used methods are as follows:
This method permits the insertion of an element at a specific index within the list.
my_list = [1, 2, 3]
my_list.insert(1, 5)
# Inserts 5 at index 1
print(my_list)
# Output: [1, 5, 2, 3]
Two lists can be concatenated to combine elements from one list with another.
my_list = [1, 2, 3]
new_elements = [4, 5]
my_list += new_elements
# Concatenates the two lists
print(my_list)
# Output: [1, 2, 3, 4, 5]
© Aptech Limited
Code Snippet 7 shows the program to add elements to Python list.
Code Snippet 7:
def main():
# Initialize an empty list
my_list = []
if element.lower() == 'done':
break
my_list.append(element)
if new_element.lower() == 'done':
break
my_list.append(new_element)
if __name__ == "__main__":
main()
In Code Snippet 7, a program for adding elements to a list through user input is
shown, detailing the process of dynamically expanding a list based on user-
provided values.
Figure 5.6 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 5.6: Output of Code Snippet 7
© Aptech Limited
Both methods achieve same result, but one must note that using reverse()
method modifies the original list, whereas slicing creates a new reversed list
without changing original list.
© Aptech Limited
3. Using Del Statement
The del statement can be used to remove elements by specifying the index
or a slice.
my_list = [10, 20, 30, 40, 50]
del my_list[1]
# Deletes the element at index 1 (20)
print(my_list)
# Output: [10, 30, 40, 50]
del my_list[1:3]
# Removes elements from index 1 to 2 ([30, 40])
print(my_list)
# Output: [10, 50]50]
Lists can be indexed and sliced. Indexing starts from 0, indicating that the first
element in the list has an index of 0, with subsequent elements assigned indexes
incrementing by 1.Negative indices count from the end of the list, where -1
corresponds to the last element, -2 to the second-to-last, and so forth.
Code Snippet 8:
print(my_list[0])
print(my_list[-1])
print(my_list[1:4])
In Code Snippet 8, indexing and slicing to access and display specific elements
and ranges within a list are illustrated, showcasing basic list manipulation
techniques.
Figure 5.7 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 5.7: Output of Code Snippet 8
end: The index just after the last element you want to include in
the slice. The slice goes up to, but does not include, this index. In
case of exclusion, it automatically assumes the default value of
the list length.
Code Snippet 9:
original_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
© Aptech Limited
print("Slice 3:", slice3)
Code Snippet 9 utilizes slicing to extract specific portions from the original list. Slice
1 gathers elements from index 2 to 4, excluding 5. Slice 2 captures elements from
index 3 to the end. Slice 3 acquires elements from the start to index 3. Slice 4
retrieves every alternate element from index 1. Slice 5 reverses the entire list.
Figure 5.8 depicts the output of this code when executed through PyCharm.
© Aptech Limited
5.1.11 List Methods
Python lists are versatile data structures equipped with a range of built-in methods
that facilitate the execution of common list operations.
remove(item): Removes the first occurrence of the specified item from the list.
Example:
my_list = [1, 2, 3, 2]
my_list.remove(2)
# my_list is now [1, 3, 2]
© Aptech Limited
pop(index): Removes and retrieves the element at the designated index. If
no index is provided, it removes and returns the last element.
Example:
my_list = [1, 2, 3]
removed_item = my_list.pop(1)
# my_list is now [1, 3], and removed_item is 2
index(item, start, end): Returns the index of the first occurrence of the
specified item within the specified range.
Example:
my_list = [1, 2, 3, 2]
index = my_list.index(2)
# index is 1
© Aptech Limited
my_list.clear()
# my_list is now []
Table 5.1 describes some commonly used built-in functions for working with lists.
© Aptech Limited
Function Description Example
iterable are True. For result = all(my_list)
lists, this checks if all # result is False
elements evaluate to
True.
Code Snippet 10 shows the Python program for maintaining movies data using
list.
def main():
movies = [] # Initialize an empty list to store #movie
names
© Aptech Limited
while True:
print("\nMovie List Management")
print("1. Add Movie")
print("2. View Movies")
print("3. Quit")
choice = input("Enter your choice: ")
if choice == "1":
movie_name = input("Enter the movie name: ")
movies.append(movie_name)
print(f"Movie '{movie_name}' added
successfully.")
elif choice == "2":
print("\nList of Movies:")
for index, movie in enumerate(movies,
start=1):
print(f"{index}. {movie}")
elif choice == "3":
print("Exiting the program.")
break
else:
print("Invalid choice. Please select a valid
option.")
if __name__ == "__main__":
main()
In Code Snippet 10, a program for managing a movie list demonstrates adding
and viewing movies through a simple menu-driven interface, illustrating list
usage in practical applications.
Figure 5.9 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 5.9: Output of Code Snippet 10
5.2 Tuple
A tuple in Python is a collection of ordered, immutable elements. It shares
resemblances with a list; nonetheless, there are notable differences:
Immutable: Tuples are immutable, meaning their elements cannot be changed
after creation. Once a tuple is created, you cannot modify, add, or remove
elements from it.
Ordered: Lists, tuples are ordered, which means that the elements have a specific
order and can be accessed by their index.
Syntax: Tuples are defined using parentheses () and comma, to separate
elements. For example: (1, 2, 3).
© Aptech Limited
Code Snippet 11:
# Creating a tuple
my_tuple = (1, 2, 3, 4, 5)
# Slicing a tuple
slice_tuple = my_tuple[1:4] # Output: (2, 3, 4)
# Length of a tuple
length = len(my_tuple) # Output: 5
# Tuple unpacking
a, b, c, d, e = my_tuple
print(a, b, c, d, e) # Output: 1 2 3 4 5
# Nested tuples
nested_tuple = ((1, 2), (3, 4))
# Concatenating tuples
concatenated_tuple = my_tuple + (6, 7)
# Repeating a tuple
repeated_tuple = my_tuple * 3
In Code Snippet 11, creating, accessing, slicing, and manipulating tuples are
shown, along with using tuples as dictionary keys and in operations such as
concatenation and repetition, emphasizing the immutable nature of tuples.
Figure 5.10 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 5.10: Output of Code Snippet 11
Tuple Packing:
item1 = "apple"
item2 = "banana"
my_tuple = item1, item2
© Aptech Limited
Code Snippet 12:
my_tuple = (1, 2, 3, 4, 5)
In Code Snippet 12, accessing elements and slicing in tuples are demonstrated,
showing how to work with tuple elements and sub-tuples.
Figure 5.11 depicts the output of this code when executed through PyCharm.
© Aptech Limited
5.2.4 Concatenation of Tuple
Concatenation of tuples in Python involves combining two or more tuples to
create a new tuple. Tuple concatenation can be accomplished using the +
operator.
tuple1 = (1, 2, 3)
tuple2 = (4, 5, 6)
In Code Snippet 13, the concatenation of tuples is explored, with two tuples and,
showcasing how to combine multiple tuples into one.
Code Snippet 14 shows how to concatenate more than two tuples together.
tuple3 = (7, 8, 9)
In Code Snippet 14, the concatenation of tuples is explored, with three tuples
and, showcasing how to combine multiple tuples into one.
5.2.5 Slicing of Tuple
Slicing a tuple in Python operates in a manner analogous to slicing a list. Indexing
and slicing notation can be applied to retrieve specific elements or sub-tuples
from a tuple.
© Aptech Limited
Code Snippet 15:
my_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9)
© Aptech Limited
Figure 5.12: Output of Code Snippet 15
my_tuple = (1, 2, 3, 4, 5)
In Code Snippet 16, the deletion of a tuple using the del statement is shown,
highlighting the removal of tuples from memory.
Figure 5.13 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Code Snippet 17 shows the Python program on Tuple for following tasks:
Creating a tuple with information about a student.
Accessing tuple elements using indexing.
Tuple unpacking to assign tuple elements to separate variables.
Attempting to modify a tuple (which will result in a TypeError).
Code Snippet 17:
def main():
# Creating a tuple
student = ("John", 20, "Computer Science")
# Tuple unpacking
name, age, major = student
print("\nTuple Unpacking:")
print("Name:", name)
print("Age:", age)
print("Major:", major)
if __name__ == "__main__":
main()
In Code Snippet 17, tuple creation, element access, unpacking, and the
immutability of tuples are illustrated through a practical example involving student
information, demonstrating tuple's utility in storing and accessing related data
without alteration.
Figure 5.14 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 5.14: Output of Code Snippet 17
© Aptech Limited
5.3 Summary
Input for a Python list can be obtained by using the input() function to collect
values.
© Aptech Limited
5.4 Test Your Knowledge
1. Which of the following is the correct way to create an empty list in Python?
A) my_list = {}
B) my_list = []
C) my_list = ()
D) my_list = None
A) [1, 2, 3]
B) [2, 3, 4]
C) [1, 2, 3, 4]
D) [2, 3, 4, 5]
3. Which of the following methods is used to add an element to the end of a list
in Python?
A) insert()
B) add()
C) append()
D) extend()
© Aptech Limited
5. What will be the output of the following code?
my_tuple = (1, 2, 3, 4, 5)
new_tuple = my_tuple[1:4]
print(new_tuple)
A) (1, 2, 3)
B) (2, 3, 4)
C) (1, 2, 3, 4)
D) (2, 3, 4, 5)
© Aptech Limited
5.4.1 Answers to Test Your Knowledge:
1. my_list = []
2. [2,3,4]
3. append()
5. (2,3,4)
© Aptech Limited
Try It Yourself
2. Write a Python program that uses list comprehension to create a new list called
squares containing the squares of all even numbers from 1 to 10 (inclusive).
After creating the squares list, print its contents.
© Aptech Limited
4. Write a Python program that uses tuples to convert temperatures between
Celsius and Fahrenheit. Your program should do the following:
i. Creates a list of tuples, where each tuple contains a temperature in Celsius
followed by its equivalent temperature in Fahrenheit.
ii. Asks the user to input a temperature in Celsius.
iii. Using a loop, searches the list of tuples to find the corresponding Fahrenheit
temperature.
iv. Prints the input temperature in Celsius and its equivalent temperature in
Fahrenheit.
v. The formula for converting Celsius to Fahrenheit is: F = (C * 9/5) + 32.
© Aptech Limited
Learning Objectives
In this session, students will learn to:
♦ Describe dictionaries and key-value pairs
♦ Explain working with dictionaries operations
♦ Distinguish between shallow and deep copies of dictionaries
♦ Define sets using the set() constructor or curly braces {}
♦ Explain about set operations and set methods
6.1 Introduction to Dictionaries in Python
The key-value pairs in a dictionary are enclosed in curly braces {} and are
separated by colons.
Syntax of dictionaries:
my_dict = {
"key1": "value1",
"key2": "value2",
"key3": "value3",
# ...
}
Keys: Keys are the labels that are used to identify and access the corresponding
values in the dictionary. Keys must be immutable, which means they cannot be
changed after they are created. Common examples of keys include strings,
numbers, and tuples.
Values: Values are the data elements associated with each key. Values can be
of any data type, including strings, numbers, lists, other dictionaries, and more.
Following Snippet shows a basic example of Dictionaries.
Example:
Dict = {1: 'Python', 2: 'Is', 3: 'Amazing'}
print(Dict)
#Output:
{1: 'Python', 2: 'Is', 3: 'Amazing'}
© Aptech Limited
They prove especially valuable for structured data storage and retrieval when
the key-value relationship is critical.
Counting and Frequency Analysis: Dictionaries are often used to count the
occurrences of items in a list or to perform frequency analysis on a dataset. 02
Example:
student_info = {
"name": "Alice",
"age": 20,
"major": "Computer Science"
}
In above example, name, age and major are keys, and Alice, 20, and Computer
Science are their corresponding values, respectively.
© Aptech Limited
6.2 Dictionaries Using {} Key-value Pairs
Dictionaries provide a highly efficient and flexible way to manage and organize
data.
Some of the key reasons to use {} syntax to create them are as follows:
© Aptech Limited
},
"book2": {
"title": "To Kill a Mockingbird",
"author": "Harper Lee",
"year": 1960
}
}
Code Snippet 1:
© Aptech Limited
# Returns 0 (key doesn't exist, default provided)
Figure 6.1 depicts the output of this code when executed through PyCharm.
6.3.2 keys()
The keys() method provides a view encompassing all the keys within the
dictionary. This view is iterable, enabling iteration through the keys, or it can be
transformed into a list when necessary. This is valuable for scenarios where there
is a requirement to iterate through or inspect the keys independently of their
associated values.
Code Snippet 2:
© Aptech Limited
# Using the keys() method to retrieve and print the #keys
all_students = student_scores.keys()
In Code Snippet 2, the keys() method is used to list all keys in a dictionary.
Figure 6.2 depicts the output of this code when executed through PyCharm.
The view is then, converted to a list for demonstration purposes. Finally, the list of
keys is printed, providing the names of all the students in the dictionary.
6.3.3 values()
The values() method provides a display of all the values found in the dictionary.
Similar to keys(), this view is iterable and can be converted into a list. This is useful
when it is required to access the values without being concerned about the
associated keys.
Code Snippet 3:
© Aptech Limited
# Using the values() method to retrieve and print the #values
all_scores = student_scores.values()
In Code Snippet 3, the values() method is employed to list all values from a
dictionary.
Figure 6.3 depicts the output of this code when executed through PyCharm.
In Code Snippet 3, the values() method is used to retrieve a view of all the
values present in the student_scores dictionary. The view is then, converted to
a list for demonstration purposes. Finally, the list of values is printed, which gives
the scores of all the students in the dictionary.
6.3.4 items()
The items() method provides a perspective of the dictionary's key-value pairs
as tuples, where each tuple includes a key paired with its associated value. This
view is useful when you must iterate through both keys and values simultaneously,
or when you want to transform the dictionary's data structure.
Code Snippet 4:
© Aptech Limited
}
In Code Snippet 4, key-value pairs are fetched as tuples from a dictionary using
the items() method.
Figure 6.4 depicts the output of this code when executed through PyCharm.
In Code Snippet 4, the items() method is used to retrieve a view of all the key-
value pairs present in the student_scores dictionary. The view is then, converted
to a list of tuples for demonstration purposes. Finally, the list of key-value pairs is
printed, showing both the student names and their corresponding scores.
© Aptech Limited
Code Snippet 5:
In Code Snippet 5, item removal using the pop() method is demonstrated, along
with handling of non-existent keys through default values.
Figure 6.5 depicts the output of this code when executed through PyCharm.
In Code Snippet 5, the pop() method is used to retrieve and remove values based
on specific keys from the student_scores dictionary. It demonstrates both
scenarios:
• For Luis, the key exists, so student_scores.pop(Luis) removes the key-value
pair and returns the value (score) associated with Luis.
• For Diego the key does not exist, so student_scores.pop(Diego,0) removes
nothing and returns the default value (0) that was provided.
© Aptech Limited
6.3.6 Iterate Through Keys, Values, and Key-value Pairs Using Loops and
Dictionary Methods
Iterating through keys, values, and key-value pairs using loops and dictionary
methods is a common operation in Python.
Code Snippet 6:
In Code Snippet 6, iteration over keys, values, and key-value pairs in a dictionary
using loops is shown.
Figure 6.6 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 6.6: Output of Code Snippet 6
In the example, the first loop iterates through the keys using the dictionary
directly. Each iteration provides the student's name. The second loop iterates
through the values using the values() method. Each iteration provides the
student's score. The third loop iterates through the key-value pairs using the
items() method. Each iteration provides both the student's name and score as
a tuple.
Code Snippet 7:
© Aptech Limited
"grades": [92, 88, 95]
},
"Olivia": {
"age": 21,
"major": "Computer Science",
"grades": [75, 82, 79]
}
}
# Accessing and manipulating nested dictionary values
emma_major = students["Emma"]["major"] # Accessing Emma's #major
liam_grades = students["Liam"]["grades"] #Accessing Liam's #grade
olivia_age = students["Olivia"]["age"] #Accessing #Olivia's age
# Modifying nested dictionary values
students["Emma"]["grades"].append(88) # Adding a new #grade for
#Emma
# Printing the updated information
print("Emma's major:", emma_major)
print("Liam's grades:", liam_grades)
print("Olivia's age:", olivia_age)
print("Updated grades for Emma:", students["Emma"]["grades"])
Figure 6.7 depicts the output of this code when executed through PyCharm.
© Aptech Limited
6.3.8 Compare Shallow and Deep Copies of Dictionaries
© Aptech Limited
6.4 Introduction to a Set
The elements in a set do not have a fixed order. This means that you
cannot access elements in a set using an index, unlike lists or arrays.
The absence of a specific order is useful when the sequence of
Unorder elements is not relevant.
Sets are mutable, allowing for the addition and removal of elements
after their creation. This allows you to modify the contents of a set
dynamically. However, the elements themselves must be immutable
(example, numbers, strings) to ensure consistency within the set.
Mutable
Sets support various operations for set theory and set manipulation,
such as union, intersection, and difference. These operations provide
convenient ways to work with multiple sets and perform comparisons
Collection between them.
You can iterate through the elements of a set using loops. Although
the order of iteration is not guaranteed to be in the order of insertion,
it covers all the elements within the set.
Iterability
© Aptech Limited
Sets are used for tasks where uniqueness matters, such as removing
duplicates from a collection of data, checking membership of an
element, and performing mathematical operations involving sets.
Use Case
Code Snippet 8:
# Creating a set
fruits = {"apple", "banana", "orange"}
# Checking membership
print("Is 'apple' in the set?", "apple" in fruits)
In Code Snippet 8, basic set operations including element addition and removal,
membership checks, and iteration through a set are presented.
Figure 6.8 depicts the output of this code when executed through PyCharm.
© Aptech Limited
In Code Snippet, the set ‘fruits’ contain unique elements and duplicate
elements are automatically removed. The unordered nature of the set is evident
when iterating through the elements.
Code Snippet 9:
In Code Snippet 9, the set() constructor is used for creating a set in Python,
showcasing the initialization of sets from other data structures.
© Aptech Limited
In Code Snippet 10, initialization of sets using curly braces {} is demonstrated,
providing an alternative method for set creation that is concise and
straightforward.
Please note: When using curly braces to create a set, be careful not to confuse it
with creating an empty dictionary, which also uses curly braces, but with key-
value pairs.
i. Union (‘|’)
The union of two sets returns a new set containing all unique elements from both
sets. The union operator | or the union() method can be used.
© Aptech Limited
iii. Difference (‘-’)
The difference of two sets returns a new set containing elements that are in the
first set, but not in the second set. The difference operator - or the difference()
method can be used.
© Aptech Limited
In above example, the concept of a subset is explored, showing how to check if
all elements of one set are contained within another set.
i. add()
The add() method adds a single element to the set. If the element is already
present, the set remains unchanged.
ii. remove()
The remove() method removes the specified element from the set. If the element
is not found, a KeyError is raised.
© Aptech Limited
my_set.remove(2) # Removes 2 from the set
iii. discard()
The discard() method removes the specified element from the set if it exists. If
the element is not found, no error is raised.
iv. pop()
The pop() method removes and returns an arbitrary element from the set. As sets
are unordered, the specific element removed is not predictable.
In above example, the pop() method is shown, which removes and returns an
arbitrary element from the set, useful in certain algorithms.
v. clear()
The clear() method removes all elements from the set, making it empty.
Following Snippet shows an example of the clear method.
Example:
my_set = {1, 2, 3}
my_set.clear() # Clears all elements, resulting in an #empty se
In above example, the clear() method is used to remove all elements from a
set, effectively resetting it to an empty state.
© Aptech Limited
6.5 Summary
Dictionary keys must be unique and immutable, while values can be of any
data type.
Python dictionaries provide methods for fetching keys, values, items, and
executing dictionary-specific operations.
© Aptech Limited
6.6 Test Your Knowledge
4. Which dictionary method is used to retrieve the list of keys present in the
dictionary?
A) keys()
B) values()
C) items()
D) get()
6. Which set operation returns a new set containing elements that are common
to both sets?
A) Union
B) Difference
C) Intersection
D) Symmetric Difference
© Aptech Limited
6.6.1 Answers to Test Your Knowledge
© Aptech Limited
Try It Yourself
© Aptech Limited
SESSION 07
ITERATORS, GENERATORS AND DECORATORS
Learning Objectives
In this session, students will learn to:
© Aptech Limited
Code Snippet 1 shows a basic example of an Iterator.
Code Snippet 1:
class CountdownIterator:
def __init__(self, start):
self.start = start
def __iter__(self):
return self
def __next__(self):
if self.start < 0:
raise StopIteration
else:
self.start -= 1
return self.start + 1
# Using the iterator
countdown = CountdownIterator(5)
for number in countdown:
print(number)
#Output: 5 4 3 2 1 0
© Aptech Limited
7.1.2 Iterable and Iterator Protocols
The iterable protocol in Python is a fundamental concept that specifies the way
objects can be iterated over using iterations.
An object adhering to the iterable protocol is capable of producing an iterator,
which is an object used to traverse through its elements one by one.
The __iter__() method plays a crucial role in this protocol. When an object is
designed to be iterable, it should implement the __iter__() method. This
method is responsible for returning an iterator object that will be used to iterate
through the elements of the iterable.
Code Snippet 2 shows an example of Python class that implements
the__iter__()method.
Code Snippet 2:
class Countdown:
def __init__(self, start):
self.start = start
def __iter__(self):
return self
def __next__(self):
if self.start < 0:
raise StopIteration
else:
self.start -= 1
return self.start + 1
© Aptech Limited
method is implemented to return the instance itselfand the __next__() method
decrements the counter and returns the next value in the sequence until it
reaches zero. The StopIteration exception is raised when the sequence is
exhausted.
Figure 7.1 depicts the output of this code when executed through PyCharm.
The iterator protocol is a fundamental concept in Python that defines the process
of sequentially traversing objects using iterations.Objects adhering to the iterator
protocol can produce successive elements in a sequence. The primary
component of the iterator protocol is the __next__() method.
The __next__() method plays a central role in the iterator protocol. When an
object is designed to be an iterator, it must implement the __next__() method.
This method is responsible for providing the next element in the sequence during
each iteration.
the__next__() method performs two main tasks:
Returning the Next Element: It returns the next element in the sequence being
traversed. If there are no more elements, the method raises the StopIteration
exception to signal the end of the iteration.
Updating State: The method updates the internal state of the iterator to keep track
of the current position in the sequence, allowing it to retrieve the correct element
during the next iteration.
Code Snippet 3 shows a basic example of using __next__() method.
© Aptech Limited
Code Snippet 3:
class SimpleIterator:
def __init__(self, max_value):
self.max_value = max_value
self.current = 0
def __next__(self):
if self.current < self.max_value:
self.current += 1
return self.current - 1
else:
raise StopIteration
#Output:
0
1
2
3
4
© Aptech Limited
to provide, serving as a signal to terminate the iteration loop. By catching this
exception, the program can effectively recognize that all elements have been
iterated through, facilitating a controlled exit from the loop.
Code Snippet 4:
class NumberGenerator:
def __init__(self, max_value):
self.max_value = max_value
self.current = 0
def __iter__(self):
return self
def __next__(self):
if self.current < self.max_value:
self.current += 1
return self.current - 1
else:
raise StopIteration
#Output:
0
1
2
3
4
© Aptech Limited
7.1.4 Common Data Structures in Python Enabling Iteration
Common Python data structures such as lists, dictionaries, sets, and strings are
iterable, which means you can traverse through their elements using iterations.
This is made possible by their adherence to the iterable protocol, which involves
implementing the __iter__() method that returns an iterator object. Here is how
each of these data structures is iterable:
i. List
Lists in Python are ordered collections of elements that are iterable.The for loop
or other iteration constructs can be used to loop through each element in the list.
Lists maintain the order of elements, so iterating through a list ensures that
elements are processed in the same order they were added.
ii. Dictionaries
Dictionaries are unordered collections of key-value pairs. When iterating through
a dictionary, the process involves iterating through its keys. Dictionaries are
inherently iterable, allowing looping through keys using for key in dictionary.
iii. Sets
In Python, sets represent unsorted groupings of distinct elements such as
dictionaries. When they are iterated through a set, you are actually iterating
through its elements. Sets maintain uniqueness, so iterating through a set ensures
that each unique element is processed only once.
iv. String
Strings are sequences of characters and each character can be considered an
element. Strings are iterable character by character, meaning you can use
iterations to traverse through each character in the string.
Code Snippet 5 shows iteration for each of the data structure.
Code Snippet 5:
# List iteration
my_list = [1, 2, 3, 4, 5]
for item in my_list:
print(item)
# Output:
1
2
3
4
© Aptech Limited
5
# Dictionary iteration
my_dict = {'a': 3, 'b': 4, 'c': 5}
for key in my_dict:
print(key, my_dict[key])
# Output:
a 3
b 4
c 5
# Set iteration
my_set = {5, 6, 7, 8, 9}
for element in my_set:
print(element)
# Output:
5
6
7
8
9
# String iteration
my_string = "Hello"
for char in my_string:
print(char)
# Output:
H
e
l
l
o
© Aptech Limited
Following examples demonstrate the use of for loop to iterate over different types
of iterables:
i. Lists
Lists are one of the most common iterables. The for loop goes through each item
within the list.
Code Snippet 6:
# Define a list
my_list = [1, 2, 3, 4, 5]
ii. Dictionaries
When iterating through dictionaries, the loop iterates through the keys by default.
You can access the corresponding values using the keys.
Code Snippet 7:
# Define a dictionary
my_dict = {'a': 3, 'b': 4, 'c': 5}
# Iterate over the dictionary and print each key-value
pair
for key in my_dict:
print(key, my_dict[key])
# Output:
a 3
b 4
c 5
iii. Sets
Sets are iterated similarly to lists, but remember that sets are unordered, so the
order of iteration is not guaranteed.
© Aptech Limited
Code Snippet 8:
# Define a set
my_set = {5, 6, 7, 8, 9}
iv. Strings
Strings are sequences of characters and the for loop iterates through each
character in the string.
Code Snippet 9:
# Define a string
my_string = "Hello"
v. User-Defined Iterables
Custom iterables can be created by defining classes that implement the iterable
protocol. This involves having an __iter__() method that returns an iterator
object and the iterator object must have a __next__() method.
© Aptech Limited
Code Snippet 10:
class MyIterable:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index < len(self.data):
value = self.data[self.index]
self.index += 1
return value
else:
raise StopIteration
#Output: 10 20 30
© Aptech Limited
7.2.1 Key Characteristics of Generators
Lazy Evaluation: Generators use lazy evaluation, which means that they generate
and yield values only when requested during iteration. This contrasts with creating
a complete list of values upfront, which can consume a significant amount of
memory.
Memory Efficiency: Since generators do not store the entire sequence of values in
memory, they are memory-efficient. This makes them suitable for processing large
datasets or infinite sequences.
State Retention: Generators retain their internal state between iterations. This allows
you to resume iteration from where it left off, making them useful for scenarios
where maintaining context is important.
Simple Syntax: Generators are defined using a function with the yield keyword
instead of the return keyword. When the generator function is called, it returns a
generator object that can be iterated over.
def fibonacci_generator():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
#Output:
0
1
© Aptech Limited
1
2
3
5
8
13
21
34
© Aptech Limited
Regular Functions Generator Functions
without creating a large collection
upfront.
State: Regular functions do not inherently State: Generator functions retain their
maintain state between calls. Each time a internal state between iterations. This
regular function is called, it starts allows you to resume the function's
executing from the beginning. execution from where it left off.
def get_squares(n):
result = []
for i in range(n):
result.append(i ** 2)
return result
squares = get_squares(5)
for square in squares:
print(square)
#Output: 0 1 4 9 16
Code Snippet 13 Iterating over the generator object squares_gen yields the
squares of numbers from 0 to n-1, printing each square
def generate_squares(n):
© Aptech Limited
for i in range(n):
yield i ** 2
squares_gen = generate_squares(5)
for square in squares_gen:
print(square)
#Output: 0 1 4 9 16
def fibonacci_generator():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
#Output
0
1
© Aptech Limited
1
2
3
5
8
13
21
34
def generate_squares(n):
for i in range(n):
yield i ** 2
# Using the generator
squares_gen = generate_squares(5)
for square in squares_gen:
print(square)
#Output: 0 1 4 9 16
Generator functions in Python pause and resume execution using the yield
keyword. This behavior allows them to maintain their internal state between
iterations.
© Aptech Limited
The execution process ofGenerator Functions is as follows:
Pausing Execution with Yield
Resuming Execution
State Retention
Dueto this pausing and resuming behavior, generator functions retain their internal
state between iterations. This allows them to maintain context and generate values
efficiently without recalculating or recomputing everything from scratch.
© Aptech Limited
In this example, the generator is created with countdown_gen =
countdown_generator(5).
The first call to next(countdown_gen) starts the generatorand it executes until
the yield n line. The value 5 is yielded and returned.
The next call to next(countdown_gen) resumes execution immediately after the
yield statement. The value of n has been decremented, so 4 is yielded and
returned.
Subsequent calls to next(countdown_gen) follow the same pattern, with the
generator resuming execution where it left off and yielding the decremented
value of n until the loop ends.
© Aptech Limited
Efficient Memory Usage: Generators are memory-efficient as they
produce values one at a time and avoid storing the entire sequence,
which is crucial when handling large datasets exceeding memory
capacity.
© Aptech Limited
Generator expressions are a compact and efficient way to create generators that
generate values on-the-fly. They offer memory-efficient processing of data, are
well-suited for large datasets or infinite sequences, and follow the lazy evaluation
principle, making them an essential tool for effective Python programming.
7.3 Decorators
In this example, the measure_time decorator calculates and prints the execution
time of a function. In the second example, the uppercase_args decorator
converts all string-type input arguments to uppercase before passing them to the
decorated function. Both of these decorators modify the behavior of the
functions they are applied to without altering the original code of those functions.
import time
def measure_time(func):
def wrapper(*args, **kwargs):
© Aptech Limited
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} took {end_time -
start_time:.4f} seconds to execute")
return result
return wrapper
@measure_time
def slow_function():
time.sleep(2)
print("Function execution complete")
slow_function()
@uppercase_args
def greet(name):
return f"Hello, {name}!"
print(greet("Alice"))
print(greet("Bob"))
Figure 7.1 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Output Explanation
The line function execution complete is the output of the slow_function() call
inside the measure_time decorator. It indicates that the function execution is
complete.
The line slow_function took 2.0006 seconds to execute is the result of the
measure_time decorator. It shows the name of the function and the time it
took to execute in seconds.
The lines Hello, ALICE! and Hello, BOB! are the results of the greet function calls
after the uppercase_args decorator has modified the input arguments. The
decorator has converted the names to uppercase before greeting them.
i. Logging
Logging is essential for debugging and monitoring the behavior of functions. A
decorator can be used to automatically log information about function calls,
arguments, and results without cluttering the actual function code.
Code Snippet 18shows the application of log function.
def log_function_call(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with args:
{args}, kwargs: {kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned:
{result}") # Fixed the print statement
return result
return wrapper
@log_function_call
def add(a, b):
© Aptech Limited
return a + b
result = add(3, 5)
#Output:
Calling add with args: (3, 5), kwargs: {}
add returned: 8
When you call the add function, the decorator log_function_call logs
information about the function call.
ii. Authentication
Decorators can be used to implement authentication checks for protecting
certain functions or methods. This ensures that only authorized users can access
sensitive parts of the code.
def authenticate(func):
def wrapper(*args, **kwargs):
if is_authenticated():
return func(*args, **kwargs)
else:
return "Access denied"
return wrapper
@authenticate
def view_sensitive_data():
return "Sensitive data"
def is_authenticated():
return True # Replace with actual
#authentication logic
result = view_sensitive_data()
print(result)
© Aptech Limited
#Output: Sensitive data
iii. Memoization
Memoization is a technique to optimize functions by caching their results for
certain input arguments. Decorators can be used to automatically cache
function results and return cached results if the same arguments are
encountered again.
def memoize(func):
cache = {}
def wrapper(*args):
if args in cache:
return cache[args]
result = func(*args)
cache[args] = result
return result
return wrapper
@memoize
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(10))
# Calculates and caches Fibonacci(0) to Fibonacci(10)
© Aptech Limited
7.3.3 Decorators Using Classes Instead of Functions
Decorators can also be implemented using classes instead of functions. This
involves creating a class that acts as a decorator by implementing thecall__
method.
Code Snippet 21 shows how to create decorators using classes for logging.
class LogFunctionCall:
def __init__(self, func):
self.func = func
@LogFunctionCall
def add(a, b):
return a + b
result = add(3, 5)
Code Snippet 22 shows how to create decorators using classes for authentication.
class Authenticate:
def __init__(self, func):
self.func = func
© Aptech Limited
else:
return "Access denied"
def is_authenticated(self):
return True # Replace with the actual
#authentication logic
@Authenticate
def view_sensitive_data():
return "Sensitive data"
result = view_sensitive_data()
print(result)
Code Snippet 23 shows how to create decorators using classes for memorization.
Code Snippet 23:
class Memoize:
def __init__(self, func):
self.func = func
self.cache = {}
@Memoize
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(10))
© Aptech Limited
7.4 Summary
The iterator protocol involves the __iter__() method for returning the
iterator object and the __next__() method for retrieving successive
elements, enabling sequential iteration through a collection.
Python data structures such as lists, dictionaries, sets, and strings are
inherently iterable, allowing for easy traversal through their elements.
© Aptech Limited
7.5 Test Your Knowledge
1.What is an iterator in Python?
A) An iterator is a built-in data type in Python used for storing key-value pairs
B) An iterator is a function that performs mathematical operations on iterable
objects
C) An iterator is an object used to iterate over elements in a sequence or
collection
D) An iterator is a graphical user interface (GUI) library in Python
3.What are the purposes of the __iter__() and __next__() methods in Python
iterators?
A) __iter__() is used to define the iterator object, while __next__() is used to
fetch the next element in the iteration
B)__iter__() is used to fetch the next element in the iteration, while __next__()
is used to define the iterator object
C) Both __iter__() and __next__() are used to define the iterator object
D) Both __iter__() and __next__() are used to fetch the next element in the
iteration
4.Python data structures such as lists, dictionaries, sets, and strings are iterable.
A) True
B) False
© Aptech Limited
5.What are the main differences between regular functions and generator
functions in Python?
A) Regular functions can return multiple values, while generator functions can only
return a single value.
B) Regular functions can be paused and resumed during execution, while
generator functions cannot be paused.
C) Generator functions use the yield keyword to produce a sequence of values
lazily, while regular functions use the return keyword to provide a single result.
D) Regular functions are more memory-efficient compared to generator
functions.
© Aptech Limited
8.How can you create decorators using classes instead of functions in Python?
A) It is not possible to create decorators using classes; decorators can only be
implemented as functions
B) By defining a class with a __init__ method to initialize and a __call__
method to implement the decorator behavior
C) By creating a class with a decorate method and using it as a wrapper for the
functions you want to enhance
D) By inheriting the@decorator class and providing the desired modification logic
in the child class
© Aptech Limited
7.5.1 Answers to Test Your Knowledge
© Aptech Limited
Try It Yourself
© Aptech Limited
Learning Objectives
In this session, students will learn to:
♦ Describe a class in Python
♦ Explain the use of objects and Constructor
♦ List the Methods, Getter, and Setter
♦ Explain the use of Static Field
♦ Define and describe Garbage Collection
8.1 Introduction to OOPs
Code Snippet 1 shows basic example to define and use a class in Python.
Code Snippet 1:
class Book:
def __init__(self, title, author, year):
self.title = title
self.author = author
self.year = year
def display_info(self):
print(f"Title: {self.title}")
print(f"Author: {self.author}")
print(f"Year: {self.year}")
Code Snippet 1 defines a Book class with attributes for title, author, and year.
Instances of the class are created for two books, and their information is displayed
using the display_info method, which prints the title, author, and year.
© Aptech Limited
Figure 8.1 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Code Snippet 2:
class Car:
# Class attribute
wheels = 4
def display_info(self):
print(f"Make: {self.make}")
print(f"Model: {self.model}")
print(f"Year: {self.year}")
print(f"Number of Wheels: {Car.wheels}")
print(f"Current Speed: {self.speed} km/h")
© Aptech Limited
Code Snippet 2 defines a Car class with attributes for make, model, year, and
speed. Methods are provided to accelerate and brake the car's speed, as well
as display its information. Instances of the class are created for two cars, their
speeds are adjusted, and their information is displayed. The class attribute wheels
is also accessed and modified for both cars.
Figure 8.2 depicts the output of this code when executed through PyCharm.
ii. Methods
Methods are functions that are associated with an object and can operate on
its data. They are essentially functions that are bound to objects. An object's
methods can be called using a dot notation as well, such as
object_name.method_name().
Code Snippet 3:
class Circle:
def __init__(self, radius):
self.radius = radius
def calculate_area(self):
area = 3.14159 * self.radius * self.radius
© Aptech Limited
return area
def calculate_circumference(self):
circumference = 2 * 3.14159 * self.radius
return circumference
def display_info(self):
print(f"Circle with radius {self.radius}")
print(f"Area: {self.calculate_area()}")
print(f"Circumference:
{self.calculate_circumference()}")
Code Snippet 3 defines a Circle class with methods to calculate its area and
circumference based on the given radius. Instances of the class are created
for two circles, and their information is displayed, including the radius, area,
and circumference.
Figure 8.3 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Code Snippet 4 shows an example of using a class in Python.
Code Snippet 4:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def display_info(self):
print(f"Name: {self.name}")
print(f"Age: {self.age}")
Code Snippet 4 defines a Person class with attributes for name and age, along
with methods to set new values for these attributes and display the information.
Instances of the class are created for two persons, their initial information is
displayed, then their information is updated using the provided methods, and
finally, the updated information is displayed.
Figure 8.4 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 8.4: Output of Code Snippet 4
iv. Instantiation
Instantiation is the term used to denote the procedure of generating an
instance from a class. This involves creating a new object based on the class's
blueprint.
Code Snippet 5:
class Student:
def __init__(self, name, age, grade):
self.name = name
self.age = age
self.grade = grade
def display_info(self):
print(f"Name: {self.name}")
print(f"Age: {self.age}")
print(f"Grade: {self.grade}")
print("\nStudent 2:")
student2.display_info()
Code Snippet 5 defines a Student class with attributes for name, age, and grade,
along with a method to display this information. Instances of the class are created
© Aptech Limited
for two students using the constructor, and their information is displayed using the
display_info() method.
Figure 8.5 depicts the output of this code when executed through PyCharm.
8.4 Constructors
A Constructor is a special method that gets automatically called when an object
of a class is created. It is used to initialize the attributes and perform any necessary
setup for the object. The constructor method is named __init__ and it is a
fundamental part of object-oriented programming in Python.
Code Snippet 6:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def display_info(self):
print(f"Name: {self.name}")
print(f"Age: {self.age}")
© Aptech Limited
# Creating instances using the constructor
person1 = Person("Alice", 25)
person2 = Person("Bob", 30)
Code Snippet 6 defines a Person class with attributes for name and age, along
with a method to display this information. Instances of the class are created for
two persons using the constructor, and their information is displayed using the
display_info() method.
Figure 8.6 depicts the output of this code when executed through PyCharm.
8.5 Methods
Methods are functions that are defined within a class and are associated with
instances of that class. Methods allow you to define actions that can be
performed on the objects created from the class. They operate on the data
(attributes) of the class instances and can modify their state or provide useful
functionality.
Methods are an essential part of object-oriented programming, allowing you to
encapsulate behavior within your class definitions. They can access and
manipulate the instance attributes and perform various operations related to the
class.
Instance Method
These methods are associated with instances of a class and have access to
instance attributes and other instance methods. The first parameter of an
instance method is always self, which refers to the instance itself.
© Aptech Limited
Class Method
These methods are defined using the @classmethod decorator. They take the
class itself as their first parameter (cls) and can be used to operate on class-level
attributes or perform actions related to the entire class.
Static Method
These methods are defined using the @staticmethod decorator. They do not
have access to the instance or class attributes and behave as regular functions
that are part of the class namespace. They are usually used for utility functions
that are related to the class but do not require access to its state.
Code Snippet 7:
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def calculate_area(self):
return self.width * self.height
@classmethod
def create_square(cls, side):
return cls(side, side)
@staticmethod
def is_large_rectangle(rect):
return rect.width * rect.height > 50
© Aptech Limited
print(f"Is rectangle1 large? {is_large1}")
print(f"Is rectangle2 large? {is_large2}")
Figure 8.7 depicts the output of this code when executed through PyCharm.
Accessor Method
A getter method is used to retrieve the value of an
Getter
attribute. It provides controlled access to the attribute,
Method
allowing you to implement validation or additional logic
by returning the value.
Mutator Method
Setter A setter method is used to set the value of an attribute. It
Method provides controlled assignment of the attribute, allowing
you to implement validation or other operations by
actually setting the value.
© Aptech Limited
Code Snippet 8 shows the use of getter and setter methods in Python.
Code Snippet 8:
class Person:
def __init__(self, name, age):
self._name = name
# Prefixing with underscore indicates a "protected" attribute
self._age = age
# Getter methods
def get_name(self):
return self._name
def get_age(self):
return self._age
# Setter methods
def set_name(self, new_name):
if isinstance(new_name, str):
self._name = new_name
else:
print("Invalid name format.")
def display_info(self):
print(f"Name: {self._name}")
print(f"Age: {self._age}")
© Aptech Limited
# Display information using display_info method
person.display_info()
In Code Snippet 8, getter methods (get_name and get_age) and setter methods
(set_name and set_age) are defined for the Person class. These methods allow
controlled access to the attributes _name and _age, enforcing validation rules by
modifying or retrieving the attribute values.
Figure 8.8 depicts the output of this code when executed through PyCharm.
Code Snippet 9:
class Dog:
species = "Canine" # This is a class attribute
© Aptech Limited
# Access class attribute using the class name
print(f"{dog1.name} is a {Dog.species}")
print(f"{dog2.name} is a {Dog.species}")
Code Snippet 9 defines a Dog class with a class attribute species set to Canine.
Instances of the class are created for two dogs, each with a name and age. The
class attribute species is accessed using the class name Dog, and it is printed
along with each dog's name. This demonstrates that the species attribute is
shared among all instances of the Dog class.
Figure 8.9 depicts the output of this code when executed through PyCharm.
Here, both dog1 and dog2 instances share the same species value because it is
a class attribute. Modifying the class attribute will affect all instances.
print(f"{dog1.name} is a {Dog.species}")
print(f"{dog2.name} is a {Dog.species}")
Code Snippet 10 modifies the class attribute species of the Dog class to Canis
familiaris. This demonstrates that modifying the class attribute affects all
instances of the class, as both dog1 and dog2 now have the updated species
value.
Figure 8.10 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 8.10: Output of Code Snippet 10
Inner classes are used to logically group classes together when one class is closely
related to another and would not make sense to be used outside of that context.
Code Snippet 11 shows an example of how to define and use an inner class in
Python.
class Outer:
def __init__(self):
self.outer_attr = "Outer attribute"
self.inner_instance = self.Inner()
# Creating an instance of Inner class
def outer_method(self):
print("This is an outer method")
class Inner:
def __init__(self):
self.inner_attr = "Inner attribute"
def inner_method(self):
print("This is an inner method")
© Aptech Limited
# Accessing inner class attributes and methods #through the
inner instance
print(outer_instance.inner_instance.inner_attr)
outer_instance.inner_instance.inner_method()
In Code Snippet 11, Inner is an inner class within the Outer class. The Inner class
is defined inside the scope of the Outer class, and it can access attributes and
methods of the outer class. The outer class can also create instances of the
inner class.
Figure 8.11 depicts the output of this code when executed through PyCharm.
Reference Counting
Each object in Python has a reference count associated with it. A reference count
represents the number of references (variables, attributes, and so on) pointing to that
object. When an object's reference count drops to zero, it means there are no more
references to that object and it becomes eligible for garbage collection.
© Aptech Limited
Cycle Detection
While reference counting is effective in most cases, it cannot handle circular
references, where objects reference each other in a cycle. To deal with circular
references, Python's garbage collector uses a cycle detection algorithm that
identifies and collects such cycles of objects.
import gc
class Person:
def __init__(self, name):
self.name = name
print(f"Created instance for {self.name}")
def __del__(self):
print(f"Deleted instance for {self.name}")
© Aptech Limited
Code Snippet 12 demonstrates manual garbage collection in Python using the
gc module. Instances of the Person class are created, references are manually
broken, and garbage collection is triggered to reclaim memory.
Figure 8.12 depicts the output of this code when executed through PyCharm.
This output demonstrates the lifecycle of the Person instances and the working
of the garbage collection process. Remember that this is just an example to
illustrate how manual garbage collection can be triggered using the gc module.
In most cases, Python's automatic garbage collection handles memory
management effectively without manual intervention.
© Aptech Limited
8.9 Summary
© Aptech Limited
8.10 Test Your Knowledge
1. Which of following statements accurately describes a class in Python?
a) A class is a built-in data type in Python used for storing collections of items
b) A class is a function in Python used to perform mathematical operations
c) A class is a blueprint that defines the structure and behavior of objects
d) A class is a special module used for importing external libraries in Python
© Aptech Limited
7. What is the purpose of garbage collection in Python?
a) To manually delete objects that are no longer required
b) To automatically release system resources used by Python programs
c) To remove unused code segments from the source code
d) To optimize the performance of mathematical calculations
© Aptech Limited
8.10.1 Answers to Test Your Knowledge
4. A function defined within a class that operates on class attributes and provides
specific functionality.
© Aptech Limited
Try It Yourself
3. Add a static field called total_people to the Person class that keeps track
of the total number of Person instances created. Update the constructor to
increment this field each time a new Person instance is created. Print the
total_people field to see how many Person instances have been created.
4. Create a class called Car with a constructor that takes a make and model as
parameters and initializes instance variables. Add a method called
start_engine that prints ‘Engine started.’ Now, create several instances of
the Car class and store them in a list. Once done, remove some of the
instances from the list. Observe how Python's garbage collection works when
objects are no longer referenced.
© Aptech Limited
Learning Objectives
In this session, students will learn to:
♦ Explain the concept of Encapsulation
♦ Explain briefly about Inheritance
♦ Outline the concept of Polymorphism
♦ Describe about Abstraction in Python
♦ Identify Exception Handling
♦ Describe the concepts of Assertions and Logging in Python
9.1 Introduction to Encapsulation
Encapsulation refers to the bundling of data (attributes) and methods (functions)
that operate on the data into a single unit, known as a class. The main goal of
encapsulation is to hide the internal details of an object's implementation from
the outside world and provide a clear interface for interacting with the object.
© Aptech Limited
By encapsulating attributes and providing controlled access through getter and
setter methods, encapsulation ensures that the internal state of an object remains
consistent and secure. This practice promotes code maintainability and makes it
easier to debug and maintain the application over a period of time. Additionally,
encapsulation enhances data integrity, as changes to attributes are made
through methods that can implement checks and safeguards against incorrect
data manipulation.
Code Snippet 1:
class BankAccount:
def __init__(self, account_number, balance):
self.__account_number = account_number
# Private attribute
self.__balance = balance # Private attribute
def get_balance(self):
return self.__balance
© Aptech Limited
Figure 9.1 depicts the output of this code when executed through PyCharm.
9.2 Inheritance
Inheritance allows a new class (subclass or derived class) to inherit attributes and
behaviors (methods and fields) from an existing class (superclass or base class).
Inheritance promotes code reusability, extensibility, and the creation of
specialized classes based on existing ones.
Superclass and Subclass Relationship: The superclass is the class being inherited
from and the subclass is the class inheriting from the superclass. The subclass
can access all public attributes and methods of the superclass, effectively
extending its functionality.
Syntax: Creating a subclass involves defining it with the class keyword, followed
by the subclass name and the superclass name enclosed in parentheses. Its
syntax is given below.
class SubclassName(SuperclassName):
# class definition
© Aptech Limited
Inheritance Hierarchy: Inheritance can create a hierarchy of classes with
multiple levels of inheritance. Subclasses can themselves become superclasses
for further subclasses, forming a chain of inheritance.
Access Control: Subclasses can access public attributes and methods of the
superclass. However, private attributes are not inherited, as they are meant to
be encapsulated within the defining class.
Code Snippet 2:
class Vehicle:
def __init__(self, make, model):
self.make = make
self.model = model
def get_info(self):
return f"Make: {self.make}, Model: {self.model}"
class Car(Vehicle):
def __init__(self, make, model, year):
super().__init__(make, model)
self.year = year
def get_info(self):
return f"Year: {self.year}, {super().get_info()}"
class ElectricCar(Car):
def __init__(self, make, model, year, battery_capacity):
super().__init__(make, model, year)
self.battery_capacity = battery_capacity
def get_info(self):
return f"Battery Capacity: {self.battery_capacity},
{super().get_info()}"
© Aptech Limited
In Code Snippet 2, the Vehicle class is the superclass with a make and model
attribute and a method to get basic information.
The Car class is a subclass of Vehicle, with an added year attribute and an
overridden get_info method.
The ElectricCar class is a subclass of Car, adding a battery_capacity
attribute and overriding the get_info method.
Figure 9.2 depicts the output of this code when executed through PyCharm.
9.3 Polymorphism
Polymorphism describes the capacity of various classes or objects to react to a
shared method name in unique manners. This facilitates the treatment of objects
from different classes as instances of a shared superclass. It fosters adaptability,
reusability of code, and the concept of abstraction.
Code Snippet 3:
class MathOperations:
def add(self, a=None, b=None, c=None):
© Aptech Limited
if c is not None:
return a + b + c
elif b is not None:
return a + b
elif a is not None:
return a
else:
return 0
Some of the important factors about Runtime or Method Overriding are as follows:
© Aptech Limited
the superclass's method. This allows objects of different classes to use the same
method name while exhibiting behavior that is tailored to their specific class.
Code Snippet 4:
class Country:
def official_language(self):
return "No official language specified"
class USA(Country):
def official_language(self):
return "English"
class China(Country):
def official_language(self):
return "Mandarin"
class Germany(Country):
def official_language(self):
return "German"
© Aptech Limited
print(country.official_language()) # Output: No official
language specified
print(usa.official_language()) # Output: English
print(china.official_language()) # Output: Mandarin
print(germany.official_language()) # Output: German
9.4 Abstraction
In Python programming, abstraction is a fundamental principle of OOP where
intricate real-world entities are depicted using simplified representations within
software. This approach concentrates on delineating the vital attributes and
functionalities of an object while concealing extraneous intricacies. Abstraction
facilitates the creation of a distinct division between the user-facing interface
and the concealed implementation specifics.
© Aptech Limited
Hiding Complexity: Abstraction allows developers to hide the intricate
implementation details of an object, exposing only the necessary interfaces
and functionalities.
Code Snippet 5:
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius * self.radius
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
© Aptech Limited
def area(self):
return self.width * self.height
Code Snippet 5 defines an abstract base class Shape with an abstract method
area(), which must be implemented by its subclasses.
Subclasses Circle and Rectangle inherit from Shape and implement the
area()method to calculate the area of a circle and a rectangle, respectively.
Instances of the Circle and Rectangle classes are created, and their area()
methods are called to calculate and print the areas of a circle and a rectangle.
Figure 9.3 depicts the output of this code when executed through PyCharm.
In this example, the Shape class is defined as an Abstract Base Class (ABC)
with an abstract method area().
The Circle and Rectangle classes are subclasses of Shape and provide their
own implementations of the area() method.
© Aptech Limited
Key concepts of exception handling in Python are as follows:
Code Snippet 6:
# Main program
numerator = input("Enter the numerator: ")
denominator = input("Enter the denominator: ")
Code Snippet 6 shows a Python script which prompts the user to input a
numerator and denominator. It then divides the numerator by the
denominator, handling division by zero and invalid input gracefully,
and prints the result or error message accordingly.
Figure 9.4 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 9.4: Output of Code Snippet 6
ii. Try-Except Block: The try block is used to enclose the code that might
raise an exception. If an exception occurs within the try block, the
program flow is immediately transferred to the corresponding except
block.
Code Snippet 7:
# Main program
try:
numerator = float(input("Enter the numerator: "))
denominator = float(input("Enter the denominator: "))
© Aptech Limited
Code Snippet 7 shows Python script that divides two numbers provided by the
user, handling division by zero and invalid input. It then prints the result or
appropriate error messages. Exception handling is utilized for specific error cases
such as invalid input or keyboard interrupts.
Figure 9.5 depicts the output of this code when executed through PyCharm.
iii. Except Clause: The except block follows the try block and contains the
code that should execute when a specific exception occurs. It is feasible
to employ multiple except blocks for the purpose of dealing with different
sorts of exceptions. This technique permits the handling of various types
of errors that could arise within a try block, facilitating specific and
targeted error management.
Code Snippet 8:
def read_and_divide(filename):
try:
with open(filename, 'r') as file:
numerator = float(file.readline())
denominator = float(file.readline())
result = numerator / denominator
return result
except FileNotFoundError:
print(f"Error: File '{filename}' not found.")
except ZeroDivisionError:
print("Error: Division by zero is not allowed.")
except ValueError:
© Aptech Limited
print("Error: Invalid data in the file. Please ensure
numeric values.")
except Exception as e:
print(f"An error occurred: {e}")
# Main program
file_name = input("Enter the name of the file: ")
result = read_and_divide(file_name)
if result is not None:
print("Result:", result)
Code Snippet 8 shows Python script that reads two numbers from a file specified
by the user, divides them, and prints the result. It handles various exceptions such
as file not found, division by zero, invalid data in the file, and
general exceptions. If successful, it prints the result of the division.
num.txt:
5
2
Figure 9.6 depicts the output of this code when executed through PyCharm.
iv. Handling Multiple Exceptions: You can use multiple except blocks to
handle different exceptions individually. This prevents your program from
crashing due to unhandled exceptions.
© Aptech Limited
Code Snippet 9:
class BookInventory:
def __init__(self):
self.books = {"Python Basics": 10, "Data Science
Handbook": 5, "Algorithms Unlocked": 3}
# Main program
inventory = BookInventory()
while True:
book_title = input("Enter the title of the book you want to
borrow: ")
if book_title.lower() == "exit":
break
inventory.borrow_book(book_title)
Figure 9.7 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 9.7: Output of Code Snippet 9
v. Finally Block: Optionally, you can include a finally block after try and
except blocks. The code in the finally block is executed regardless of
whether an exception was raised or not.
class BikeRental:
def __init__(self, total_bikes):
self.total_bikes = total_bikes
self.available_bikes = total_bikes
def rent_bike(self):
try:
if self.available_bikes > 0:
print("You have rented a bike.")
self.available_bikes -= 1
else:
print("Sorry, no bikes available for rent.")
except Exception as e:
print(f"An error occurred: {e}")
finally:
print("Thank you for using our bike rental
service!")
# Main program
rental_shop = BikeRental(total_bikes=10)
© Aptech Limited
while True:
user_input = input("Press 'r' to rent a bike or 'q' to quit:
")
if user_input.lower() == 'q':
break
elif user_input.lower() == 'r':
rental_shop.rent_bike()
else:
print("Invalid input. Please enter 'r' or 'q'.")
Code Snippet 10 shows a Python class BikeRental manages the rental of bikes,
with the ability to rent a bike if available and decrement the available bikes
count. It ensures availability is checked before renting and provides appropriate
feedback. The main program interacts with users, allowing them to rent bikes (r)
or quit (q).
The try-except-finally block ensures error handling and concludes with a thank
you message regardless of the outcome.
Figure 9.8 depicts the output of this code when executed through PyCharm.
© Aptech Limited
except KeyboardInterrupt:
print("\nKeyboard Interrupt detected. Exiting.")
def read_integer():
try:
num = int(input("Enter an integer: "))
return num
except ValueError:
print("Invalid input. Please enter a valid integer.")
return None
def open_file(filename):
try:
file = open(filename, 'r')
content = file.read()
file.close()
return content
except FileNotFoundError:
return "Error: File not found"
except Exception as e:
return f"An error occurred while reading the file: {e}"
# Main program
try:
numerator = read_integer()
denominator = read_integer()
© Aptech Limited
print("Division result:", result)
Code Snippet 11 shows a Python script that defines functions for division, reading
an integer, and opening a file. It handles various exceptions such as division
by zero, invalid data types, and file not found. In the main program, it
prompts users for input, performs division, reads a file, and displays results or errors.
Finally, it prints a completion message after execution.
num.txt:
5
2
Figure 9.9 depicts the output of this code when executed through PyCharm.
© Aptech Limited
condition is false, an exception (AssertionError) is raised, indicating that
something is wrong with your code and requires attention.
Assertions are typically used during development and testing to catch logical
errors and ensure that assumptions about the code's behavior are valid. They help
to identify problems early in the development process.
Figure 9.10 depicts the output of this code when executed through PyCharm.
© Aptech Limited
9.6.2 Logging in Python
Logging in Python involves recording events, messages, and other relevant
information during the execution of a program. Instead of printing messages to
the console, logging provides a more structured and versatile approach to
capturing information about a program's behavior.
import logging
# Configure logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s -
%(levelname)s - %(message)s')
result = divide(10, 2)
result = divide(10, 0) # This will generate a
#ZeroDivisionError, and the log messages will capture it
Figure 9.11 depicts the output of this code when executed through PyCharm.
© Aptech Limited
In this instance, the logging messages incorporate timestamps, log levels, and the
actual content of the messages. The logging level can be modified to regulate
which messages are captured. For instance, configuring the level to logging.
INFO will display only the info and higher-level messages in the output.
© Aptech Limited
9.7 Summary
© Aptech Limited
9.8 Test Your Knowledge
1. Which concept in Python involves bundling data and methods that operate
on that data into a single unit, while restricting direct access to the internal details
from outside the unit?
A) Inheritance
B) Polymorphism
C) Abstraction
D) Encapsulation
© Aptech Limited
5. What are assertions and logging used for in Python?
A) Assertions are used to create custom error messages, while logging is used to
handle input/output operations
B) Assertions are used to print debug information, while logging is used for
controlling program flow
C) Assertions are used to terminate the program when an error occurs, while
logging is used for recording information and debugging messages
D) Assertions are used to optimize code execution, while logging is used for
handling file operations
© Aptech Limited
9.8.1 Answers to Test Your Knowledge
1. Encapsulation
2. The ability of objects to take on multiple forms.
3. The process of hiding complex implementation details and showing only the
necessary features of an object.
4. NumberEfigurerror
5. Assertions are used to terminate the program when an error occurs, while
logging is used for recording information and debugging messages.
© Aptech Limited
Try It Yourself
1. Create a program that models a simple zoo.
a) Create a base class called Animal with following attributes: name,
species, and sound.
b) Create a constructor for the Animal class that initializes these
attributes.
c) Create a method in the Animal class called make_sound that prints
the sound of the animal.
d) Create two subclasses: Mammal and Bird, which inherit from the
Animal class.
e) Add an additional attribute to the Mammal class called num_legs.
f) Add an additional attribute to the Bird class called can_fly.
g) Create constructors for both Mammal and Bird classes that initialize
their respective attributes and call the constructor of the base class
(Animal).
h) Override the make_sound method in both the Mammal and Bird
classes to print an appropriate sound for each animal type.
i) Create instances of both Mammal and Bird classes and demonstrate
the use of the make_sound method.
© Aptech Limited
Learning Objectives
Encoding Data: The data is encoded or transformed into a format that is easy
to represent and transmit. Common serialization formats in Python include JSON,
pickle, XML, and YAML. Among these, JSON and pickle are widely used.
Storage and Transmission: The serialized data can be saved to a file, sent over
a network, or stored in a database. JSON files, for example, can be easily read
by other programming languages as well. It makes it a popular choice for cross-
language communication.
© Aptech Limited
However, it is important to choose the appropriate serialization format based on
factors such as human readability, cross-language compatibility, and security.
Code Snippet 1 shows a basic example that demonstrates serialization using the
JSON format.
Code Snippet 1:
import json
In Code Snippet 1, the code converts a Python dictionary into JSON format using
json.dumps(). It then prints the serialized data and saves it to a file named
data.json. This Snippet demonstrates the process of serializing Python data to
JSON and saving it for storage or transmission.
Figure 10.1 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 10.1: Output of Code Snippet 1
Structured Data Retrieval: The serialized data, which will be in the form of a string
(for JSON) or binary data (for pickle), is obtained from a file, network
transmission, or any storage medium.
© Aptech Limited
Data Restoration: The deserialized data is now in a state that closely resembles
the original data structure that was initially serialized. This data can now be
utilized, processed, and manipulated as required within the Python program.
Code Snippet 2:
import json
Figure 10.2 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 10.2: Output of Code Snippet 2
© Aptech Limited
Aspect Serialization Deserialization
- JSON, XML, YAML (for human-
readable structured data). Depends on the serialization
- Pickle (for Python-specific format used. Formats such as
Serialization
objects). JSON, pickle, XML, and so on
Formats - Protocol Buffers, Avro, have their deserialization
and so on (for various use methods.
cases).
© Aptech Limited
Applications Serialization Deserialization
Preparing data to be passed Accepting and interpreting
Inter-process between different processes or data received from other
Communication components of an application. processes or components.
Table 10.2: Common Scenarios where Serialization and Deserialization are utilized
© Aptech Limited
10.5 Standardized Data Formats for Serialization and Deserialization
Using standardized data formats such as JSON and Extensible Markup Language
(XML) for serialization and deserialization offers several benefits, especially in terms
of interoperability between different systems, programming languages, and
platforms.
Cross-Platform Compatibility:
Standardized formats such as JSON and XML are platform-agnostic. It means
they can be used across various operating systems and hardware architectures.
This compatibility ensures that serialized data can be exchanged seamlessly
between systems running on different platforms.
Language Neutrality:
JSON and XML are not tied to a specific programming language. They are
supported by a wide range of programming languages, making it easier to
share data between applications developed in different languages. This
language neutrality promotes interoperability.
Versatility:
JSON and XML are versatile and can represent a wide range of data structures,
including nested objects, arrays, and primitive data types. This flexibility allows
for the serialization of diverse data formats, making them suitable for a broad
spectrum of use cases.
© Aptech Limited
10.6 Role of Exception and Error Handling in Serialization and
Deserialization
Security Concerns: Exception handling is also important for security. It can help
detect and respond to potential security threats. For instance, attempts to inject
malicious data or exploit vulnerabilities in the deserialization process.
© Aptech Limited
10.7 Summary
© Aptech Limited
10.8 Test Your Knowledge
1. What is serialization in Python?
A) The process of converting data into a format suitable for storage or transmission
B) The process of executing code serially
C) The process of converting a string into an integer
D) The process of encrypting data
© Aptech Limited
10.8.1 Answers to Test Your Knowledge
© Aptech Limited
Try It Yourself
1. Create a program that serializes and deserializes a Python object using the
pickle module.
a) Create a Python class called Person with attributes for a person's name
and age.
b) Write a function called serialize_person that takes a Person object as
input and serializes it using the pickle module. Save the serialized object
to a file named ’person.pickle’.
c) Write a function called deserialize_person that reads the serialized
object from ‘person.pickle’ and deserializes it, returning the Person
object.
d) Prompt the user to enter a person's name and age.
e) Create a Person object with the provided information.
f) Use the serialize_person function to serialize the Person object.
g) Use the deserialize_person function to read and deserialize the object
from the file.
h) Display the deserialized Person object's attributes to verify that the
serialization and deserialization were successful.
© Aptech Limited
Learning Objectives
© Aptech Limited
Persistence: Storing and retrieving complex data structures,
such as dictionaries, lists, and objects, for long-term use or
sharing between program executions.
Code Snippet 1 shows a basic example of files using the built-in functions.
Code Snippet 1:
© Aptech Limited
with open("example.txt", "r") as file:
file_contents = file.read()
In Code Snippet 1, the code creates a text file named ‘example.txt’ and writes
two lines of text into it. Then, it reads the contents of the file and stores them in a
variable called file_contents. Finally, it prints the contents of the file to the
console.
Figure 11.1 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Writing Data:
Utilize the write() method of the file object to write the string data to the file.
Closing a File:
Always close the file after writing to ensure that changes are saved and resources
are freed. This can be done automatically using the ‘with’ statement, as shown
in the previous code.
Code Snippet 2:
In Code Snippet 2, it opens a file named ‘example.txt’ in write mode (w) and
writes the specified string to it. The ‘with’ statement ensures that the file is properly
closed after writing, even if an error occurs during the process.
Reading Data:
You can read the file contents using various methods such as read(),
readline(), or by iterating over the file object.
© Aptech Limited
To read the entire file as a string, refer to example given below.
Example:
with open(file_path, 'r') as file:
file_contents = file.read()
To read the file content line by line, following example can be referred.
Example:
with open(file_path, 'r') as file:
for line in file:
# Process each line
print(line)
Closing a File:
Similar to the process of writing, it is important to close the file after reading to
release system resources. The ‘with’ statement handles this automatically.
Code Snippet 3:
try:
with open(file_name, 'r') as file:
# Read the contents of the file into a string
file_contents = file.read()
print("Contents of the file:")
print(file_contents)
except FileNotFoundError:
print(f"The file {file_name} does not exist.")
except IOError:
print(f"An error occurred while reading from {file_name}.")
In Code Snippet 3, the code attempts to open a file named ‘example.txt’ for
reading. If the file exists, it reads its contents into a string and prints them. If the file
does not exist, it raises a FileNotFoundError, and if there is an error while reading
the file, it raises an IOError. In both error cases, it prints an appropriate error
message. The try-except blocks handle potential exceptions that may occur
during the file operation.
© Aptech Limited
Figure 11.2 depicts the output of this code when executed through PyCharm.
© Aptech Limited
untrusted sources. To mitigate this risk, it is essential to avoid unpickling data from
untrusted or unauthenticated sources.
Here is a simple example of how to utilize the pickle module to serialize and
deserialize a Python object:
Code Snippet 4 shows how to utilize Pickle module.
Code Snippet 4:
import pickle
Figure 11.3 depicts the output of this code when executed through PyCharm.
© Aptech Limited
In this example, pickle.dump() has been utilized to serialize the data dictionary
into a binary file called data.pickle and pickle.load() to deserialize it back
into the loaded_data variable.
Global Interpreter Lock (GIL): Python has a GIL that allows only one thread to
execute Python bytecode at a time. This limitation can affect multi-core
processors and may not provide full parallelism for CPU-bound tasks. However,
threads can still be beneficial for I/O-bound operations.
© Aptech Limited
managing multiple tasks concurrently, including tasks that are interleaved on a
single CPU core.
Code Snippet 5 shows creating and running a thread in Python using the
threading module.
Code Snippet 5:
import threading
def print_numbers():
for i in range(1, 6):
print(f"Number {i}")
def print_letters():
for letter in "abcde":
print(f"Letter {letter}")
Figure 11.4 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 11.4: Output of Code Snippet 5
In this program, two threads are created, each with a specific function
(print_numbers and print_letters) so that they execute concurrently. The
start() method initiates the execution of each thread, and join() is utilized to
wait for both threads to finish before continuing with the main program.
Following are some fundamental points regarding the main thread in Python:
Single Entry Point: The main thread is the initial entry point for your Python
program. When you invoke a Python script, the code within the main thread
commences execution from the beginning of the script.
© Aptech Limited
Global Scope: The main thread operates within the global scope of your script.
Variables and objects created in the global scope are accessible from the main
thread and also from any other threads that you may create.
Code Snippet 6:
import threading
def main():
print("This is the main thread.")
if __name__ == "__main__":
main_thread = threading.main_thread()
print(f"Main thread name: {main_thread.name}")
main()
In Code Snippet 6, it demonstrates how to obtain and print information about the
main thread in a Python program using the threading module. It defines a
main() function which simply prints a message indicating it is the main thread.
The if __name__ == __main__: block ensures that the main() function is called
only when the script is executed directly. Within this block, it retrieves the main
thread object using threading.main_thread() and prints its name. Finally, it
calls the main() function.
Figure 11.5 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Python supports multi-threading and multi-processing, which allows to create
additional threads or processes for concurrent tasks. Nevertheless, the main
thread retains its role as the primary starting point and often serves as the central
controller for these auxiliary threads or processes.
Code Snippet 7:
import threading
def my_function():
for i in range(5):
print(f"Function: {i}")
In Code Snippet 7, the code demonstrates how to create and start a new thread
in Python using the threading module. It defines a function my_function() that
prints numbers from 0 to 4. Then, it creates a new thread my_thread targeting
my_function() function. The thread is started using start() and then optionally
joined back using join() to wait for its completion. Finally, a message indicating
the continuation of the main thread is printed.
Figure 11.6 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 11.6: Output of Code Snippet 7
Code Snippet 8:
import threading
class MyThread(threading.Thread):
def run(self):
for i in range(5):
print(f"Class: {i}")
In Code Snippet 8, the code demonstrates how to create a custom thread class
in Python by sub-classing threading.Thread. The subclass MyThread overrides
the run() method, where the actual thread logic is defined. In this case, it prints
numbers from 0 to 4. An instance of MyThread class named my_thread is created,
© Aptech Limited
and then the thread is started using start(). Optionally, join() is used to wait
for the thread to finish execution. Finally, a message indicating the continuation
of the main thread is printed.
Figure 11.7 depicts the output of this code when executed through PyCharm.
Code Snippet 9 demonstrates the creation of a thread using class and function
in Python.
Code Snippet 9:
import threading
© Aptech Limited
# Wait for both threads to finish (optional)
function_thread.join()
class_thread.join()
Figure 11.8 depicts the output of this code when executed through PyCharm.
import threading
© Aptech Limited
11.6.2 Define a Thread Function
Create a function that represents the task you want to execute in parallel. This
function will be run by multiple threads. Refer to exemplary function given below.
Example:
def worker_thread(thread_id):
# Your code for the task goes here
Example:
thread1 = threading.Thread(target=worker_thread, args=(1,))
thread2 = threading.Thread(target=worker_thread, args=(2,))
# Create more threads as required
Example:
thread1.start()
thread2.start()
# Start more threads as required
Example:
thread1.join()
thread2.join()
# Use join() for other threads as required
import threading
© Aptech Limited
def worker_thread(thread_id):
print(f"Thread {thread_id} started.")
# Your task code here
print(f"Thread {thread_id} finished.")
# Start threads
thread1.start()
thread2.start()
In Code Snippet 10, the code demonstrates the use of threading in Python. It
defines a function worker_thread() representing the task to be executed by
each thread. Two threads are created with unique identifiers, and they are
started to execute the worker_thread() function. The main thread waits for both
worker threads to finish their tasks using join(), and finally, it prints a message
indicating its continuation.
Figure 11.9 depicts the output of this code when executed through PyCharm.
© Aptech Limited
facilitating communication between threads which is crucial for data sharing or
coordinating activities within a multi-threaded application.
import threading
shared_variable = 0
lock = threading.Lock()
def thread1():
global shared_variable
with lock:
shared_variable += 1
def thread2():
global shared_variable
with lock:
shared_variable -= 1
In Code Snippet 11, the code demonstrates the use of threading with a shared
variable in Python. Two threads are defined, each modifying the shared variable
shared_variable within a critical section protected by a lock. thread1()
increments the shared variable while thread2() decrements it. The
threading.Lock() object ensures that only one thread can access the shared
variable at a time, preventing potential race conditions.
© Aptech Limited
11.7.2 Queue (Thread-Safe)
The queue module provides thread-safe data structures such as Queue and
PriorityQueue. These queues can be utilized for safe communication between
threads, allowing one thread to put data into the queue and another thread to
retrieve it.
import threading
import queue
q = queue.Queue()
def producer():
data = "Hello, World!"
q.put(data)
def consumer():
data = q.get()
print(data)
Condition Variables:
Condition variables (example, threading.Condition) allow threads to
synchronize their actions based on certain conditions. Threads can wait for a
condition to be met and signal when the condition changes.
import threading
condition = threading.Condition()
def thread1():
© Aptech Limited
with condition:
condition.wait()
print("Thread 1 woke up!")
def thread2():
with condition:
condition.notify()
In Code Snippet 13, threading condition objects are utilized for synchronization
between threads. The function thread1() enters a critical section protected by
the condition, where it waits for a notification using condition.wait(). Once it
receives the notification, it prints ‘Thread 1 woke up!’ On the other hand, the
thread2() function, upon execution, notifies other threads waiting on the
condition using condition.notify(). Threading condition objects enable
threads to coordinate their execution based on certain conditions, ensuring
efficient synchronization and communication between them.
Event Objects:
Event objects (example, threading.Event) can be utilized to signal between
threads. One thread sets the event, and other threads can wait for the event to
be set before proceeding.
import threading
event = threading.Event()
def thread1():
event.wait()
print("Thread 1 received the event!")
def thread2():
event.set()
In Code Snippet 14, the code showcases the usage of threading events in Python.
An event object is created using threading.Event(). In this scenario,
thread1() waits for the event to be set using event.wait(). Meanwhile,
thread2() sets the event using event.set(). Once the event is set by
thread2(), thread1() proceeds to print ‘Thread 1 received the event!’
© Aptech Limited
Threading events are synchronization primitives that allow threads to coordinate
their execution based on the state of the event.
import threading
import time
Figure 11.10 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 11.10: Output of Code Snippet 15
© Aptech Limited
11.8 Summary
For Python file tasks with strings: open in read or write mode using file
methods, and close for data integrity.
The main thread in Python is the starting execution thread responsible for
running the program's primary code.
© Aptech Limited
11.9 Test Your Knowledge
1. Which of the following methods in Python is utilized to open a file for both
reading and writing, creating the file if it does not exist?
A) open("file.txt", "r")
B) open("file.txt", "w")
C) open("file.txt", "a")
D) open("file.txt", "r+")
2. Which of the following statements is true regarding the Python pickle module?
A) Pickle is utilized for compressing files in Python
B) Pickle is utilized for converting Python objects into a byte stream
C) Pickle is utilized for sorting data in Python lists
D) Pickle is utilized for creating database connections in Python
4. In Python, you can create a thread using both a class (by sub-classing
threading.Thread) and a function (by passing the function as a target to
threading.Thread).
A) True
B) False
© Aptech Limited
6. Threads in Python can communicate and share data with each other without
any requirement for synchronization mechanisms such as locks or semaphores.
A) True
B) False
© Aptech Limited
11.9.1 Answers to Test Your Knowledge
1. open("file.txt", "r+")
2. Pickle is utilized for converting Python objects into a byte stream.
3. The thread that runs the __main__ module.
4. True
5. Multithreading can improve the responsiveness of a program by allowing
multiple tasks to run concurrently.
6. False
© Aptech Limited
Try It Yourself
1. Create a Python program that reads a text file and counts the number of
words in it.
2. Write a program that reads a CSV file containing student names and their
scores, calculates the average score, and displays the names of students who
scored above the average.
4. Build a program that simulates a simple ticket booking system with multiple
booking agents (threads). Each booking agent should try to book tickets
concurrently, and you should ensure that tickets are not overbooked.
© Aptech Limited
Learning Objectives
In this session, students will learn to:
♦ Identify steps involved in accessing HTML, Images, and Server
♦ Explain how to install and setup MySQL Database
♦ Explain how to create a Database in Python
♦ Define CREATE, READ, UPDATE and DELETE commands
♦ Describe how to download and use tools in Python
12.1 Accessing HTML, Images, and Server
Various libraries and modules are available in Python to access HTML, images,
and interact with a server. Following are some common libraries and approaches
for each task:
Code Snippet 1:
import requests
from bs4 import BeautifulSoup
# You can now work with the parsed HTML, example, #extract
data or manipulate it
print(soup.title) # Print the title of the Webpage
else:
print("Failed to retrieve HTML:", response.status_code)
Code Snippet 1 shows Python script that fetches the HTML content of
https://example.com using a HTTP GET request. If the request is successful
(status code 200), it parses the HTML using BeautifulSoup and prints the
Webpage title. Otherwise, it prints a failure message with the status code.
Figure 12.1 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 12.1: Output of Code Snippet 1
Code Snippet 2:
import requests
Code Snippet 2 shows Python script that downloads an image from the URL
https://example.com/image.jpg using an HTTP GET request with the
requests library. If the request is successful (status code 200), it saves the
image to a local file named image.jpg. If the request fails, it prints a failure
message along with the status code.
© Aptech Limited
Code Snippet 3:
import requests
Code Snippet 3 shows Python script thar sends an HTTP POST request to the server
endpoint
https://www.themealdb.com/api/json/v1/1/filter.php?i=bread with
data {"key": "value"}. If successful (status code 200), it prints the server
response in JSON format; otherwise, it prints a failure message with the status
code.
Figure 12.2 depicts the output of this code when executed through PyCharm.
© Aptech Limited
12.2 Installing and Setting up MySQL Database
To install and set up a MySQL database for Python, following steps are required
to be followed.
Linux (Ubuntu/Debian)
sudo apt-get update
sudo apt-get install mysql-server
Linux (CentOS/RHEL)
sudo yum install mysql-server
Windows
Download the MySQL Installer for Windows from the official MySQL Website and
follow the installation wizard.
On macOS or Windows, you can start it using the services or system preferences.
© Aptech Limited
Code Snippet 4:
import mysql.connector
finally:
# Close the connection when done
if connection.is_connected():
connection.close()
print("Connection closed")
Figure 12.3 depicts the output of this code when executed through PyCharm.
© Aptech Limited
12.2.5 Execute SQL Queries
With the connection established, you can execute SQL queries using a cursor
object.
Code Snippet 5:
cursor = connection.cursor()
© Aptech Limited
12.3 Creating a Database from Python
To create a database from Python, use the MySQL connector and execute SQL
commands to create the database.
Example:
import mysql.connector
try:
connection = mysql.connector.connect(**config)
if connection.is_connected():
print("Connected to MySQL server")
except mysql.connector.Error as e:
print(f"Error: {e}")
Example:
try:
© Aptech Limited
cursor = connection.cursor()
cursor.execute(create_database_query)
print("Database created successfully")
except mysql.connector.Error as e:
print(f"Error: {e}")
finally:
if connection.is_connected():
cursor.close()
Example:
if connection.is_connected():
connection.close()
print("Connection closed")
This code will create a new database with the specified name. Ensure to replace
your_database_name with the desired name for your database.
Code Snippet 6:
import mysql.connector
try:
# Connect to the MySQL server
connection = mysql.connector.connect(**config)
if connection.is_connected():
© Aptech Limited
print("Connected to MySQL server")
# Create a cursor
cursor = connection.cursor()
except mysql.connector.Error as e:
print(f"Error: {e}")
finally:
if connection.is_connected():
cursor.close()
connection.close()
print("Connection closed")
Code Snippet 6 shows Python script that connects to a MySQL server using the
provided server details and attempts to create a database with the specified
name if it does not already exist. It prints a confirmation message if the database
is created successfully. If an error occurs during the process, it prints the error
message. Finally block closes the connection and cursor.
Figure 12.4 depicts the output of this code when executed through PyCharm.
© Aptech Limited
12.4.1 CREATE (Insert Data)
This command is used to insert data into MySQL database.
Code Snippet 7:
import mysql.connector
try:
connection = mysql.connector.connect(**config)
if connection.is_connected():
print("Connected to MySQL server")
# Create a cursor
cursor = connection.cursor()
# Data to be inserted
data = ("value1", "value2")
except mysql.connector.Error as e:
print(f"Error: {e}")
finally:
if connection.is_connected():
cursor.close()
© Aptech Limited
connection.close()
print("Connection closed")
Replace your_table_name and the column names with your actual table and
column names, and value1 and value2 with the data you want to insert.
Code Snippet 7 shows Python script that connects to a MySQL database and
inserts data into a specified table. It defines an INSERT query with placeholders
for values to be inserted and executes it with the provided data. Finally, it commits
the changes to the database and closes the connection.
Figure 12.5 depicts the output of this code when executed through PyCharm.
Code Snippet 8:
import mysql.connector
try:
connection = mysql.connector.connect(**config)
if connection.is_connected():
print("Connected to MySQL server")
# Create a cursor
cursor = connection.cursor()
© Aptech Limited
# Define a SELECT query
select_query = "SELECT * FROM your_table_name"
except mysql.connector.Error as e:
print(f"Error: {e}")
finally:
if connection.is_connected():
cursor.close()
connection.close()
print("Connection closed")
Replace your_table_name with the name of the table from which you want to
retrieve data.
Code snippet 8 shows Python script that connects to a MySQL database and
retrieves all records from a specified table. It defines a SELECT query to fetch
data from the table, executes the query, and fetches all rows of data using the
fetchall() method. It then processes and prints the retrieved data. Finally, it
closes the connection to the database.
Figure 12.6 depicts the output of this code when executed through PyCharm.
© Aptech Limited
12.4.3 UPDATE
To perform an UPDATE command in Python, use the MySQL connector (or the
appropriate database library for your chosen database system). Code Snippet 9
demonstrates the process to update data in a MySQL database using the mysql-
connector-python library. Make sure the library is installed. If it is not installed,
install it using the pip command given below.
Code Snippet 9:
import mysql.connector
try:
# Connect to the MySQL server
connection = mysql.connector.connect(**config)
if connection.is_connected():
print("Connected to MySQL server")
# Create a cursor
cursor = connection.cursor()
© Aptech Limited
except mysql.connector.Error as e:
print(f"Error: {e}")
finally:
if connection.is_connected():
cursor.close()
connection.close()
print("Connection closed")
Code Snippet 9 shows Python script that connects to a MySQL database and
updates records in a specified table. It defines an UPDATE query to set a column's
value based on a condition. It then executes the query with the provided data,
and commits the changes to the database. Finally, it closes the connection.
Replace the placeholders in the config dictionary with your MySQL server details.
Replace your_table_name with the name of your table, column1 with the
column you want to update, column2 with the column by which you want to
identify the row to update, new_value with the new value, and
value_to_identify_row with the value that should identify the row to update.
Figure 12.7 depicts the output of this code when executed through PyCharm.
import mysql.connector
© Aptech Limited
"user": "your_username",
"password": "your_password",
"database": "your_database_name",
}
try:
connection = mysql.connector.connect(**config)
if connection.is_connected():
print("Connected to MySQL server")
# Create a cursor
cursor = connection.cursor()
except mysql.connector.Error as e:
print(f"Error: {e}")
finally:
if connection.is_connected():
cursor.close()
connection.close()
print("Connection closed")
Code Snippet 10 shows Python script that connects to a MySQL database and
deletes records from a specified table. It defines a DELETE query to remove rows
based on a condition. It executes the query with the provided data, and commits
the changes to the database. Finally, it closes the connection.
Replace your_table_name with the name of the table from which you want to
delete data, column_name with the column by which you want to identify the row
to delete, and value_to_delete with the value that should trigger the deletion.
Figure 12.8 depicts the output of this code when executed through PyCharm.
© Aptech Limited
Figure 12.8: Output of Code Snippet 10
Code Snippet 11 shows the process of database handling using SQLite database.
import sqlite3
© Aptech Limited
insert_query = "INSERT INTO students (name, age) VALUES (?, ?)"
data_to_insert = [("Alice", 25), ("Bob", 22), ("Charlie", 28)]
cursor.executemany(insert_query, data_to_insert)
connection.commit()
print("Data inserted successfully.")
© Aptech Limited
Code Snippet 11 demonstrates various database operations using SQLite. It
creates or connects to a SQLite database file, creates a table if it does not exist,
inserts data into the table, retrieves data, updates records, deletes records, and
finally closes the cursor and connection.
Figure 12.9 depicts the output of this code when executed through PyCharm.
12.5.1 MySQL
MySQL Connector/Python: This is the official Python driver for MySQL. This can be
utilized for connecting to a MySQL database, executing queries, and handling
data.
Import MySQL
import mysql.connector
© Aptech Limited
Establish a connection
# Replace these with your actual database credentials
host = "your_host"
user = "your_username"
password = "your_password"
database = "your_database"
# Create a connection
connection = mysql.connector.connect(
host=host,
user=user,
password=password,
database=database
)
cursor = connection.cursor()
cursor.execute(insert_query, user_data)
connection.commit()
cursor.close()
connection.close()
12.5.2 PostgreSQL
This is the most popular PostgreSQL adapter for Python. This allows connection
to a PostgreSQL database for query execution.
© Aptech Limited
pip install psycopg2
12.5.3 MongoDB
PyMongo is the official Python driver for MongoDB. It allows you to interact with
MongoDB databases.
After installing the suitable library for the database system, it enables connection
to the database, query execution, data insertion, retrieval, and additional
database operations.
# Create a session
Session = sessionmaker(bind=engine)
session = Session()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
# Create the table (if it does not exist)
Base.metadata.create_all(engine)
# Insert data
new_user = User(name='John')
session.add(new_user)
session.commit()
© Aptech Limited
# Query data
users = session.query(User).all()
for user in users:
print(user.name)
# Update data
user_to_update =
session.query(User).filter_by(name='John').first()
user_to_update.name = 'Jane'
session.commit()
# Delete data
user_to_delete =
session.query(User).filter_by(name='Jane').first()
session.delete(user_to_delete)
session.commit()
© Aptech Limited
12.6 Summary
Python utilizes libraries for HTML retrieval, image downloading, and server
interaction via HTTP requests.
© Aptech Limited
12.7 Test Your Knowledge
1. Which Python library is commonly used to access and parse HTML content from
Web pages?
A. json
B. requests
C. matplotlib
D. pandas
2. What is the primary purpose of installing MySQL Server and configuring it during
setup?
A. To create and manage databases
B. To write Python code
C. To access HTML content
D. To install additional Python libraries
4. Which of the following SQL statements is used to read data from a database in
Python when implementing CRUD operations?
A. CREATE
B. UPDATE
C. INSERT
D. SELECT
© Aptech Limited
12.7.1 Answers to Test Your Knowledge
1. requests
2. To create and manage databases
3. CREATE DATABASE
4. SELECT
5. To extend functionality and simplify development
© Aptech Limited
Try It Yourself
1. Create a Python program that reads a text file and counts the number of
words in it.
2. Write a program that reads a CSV file containing student names and their
scores, calculates the average score, and displays the names of students who
scored above the average.
3. Create a Python program that uses two threads to download multiple images
concurrently from the Internet. Save each downloaded image to a local
directory.
4. Build a program that simulates a simple ticket booking system with multiple
booking agents (threads). Each booking agent should try to book tickets
concurrently, and you should ensure that tickets are not overbooked.
© Aptech Limited
Appendix
Sr. Case Studies
No
1 Project 1: Task Manager
Scenario:
Create a task manager program with user-friendly features:
1. Add tasks to the list.
2. View current task list.
3. Delete completed tasks.
Hints:
1. Use lists to store the tasks.
2. Implement flow control statements to handle user input and execute
operations.
3. Use functions to modularize the code and improve reusability.
Technology to be used:
1. Python SDK 3.9.17
2. Tkinter
3. Pycharm 2023.1
Scenario:
Create a program that can encrypt and decrypt files using a simple
encryption algorithm. The program should perform following features.
1. Prompt user to choose: Encrypt, Decrypt, or View encrypted file.
2. Encrypt files securely with selected algorithm.
3. Decrypt and display original contents effortlessly.
Hints:
1. Encrypt/Decrypt files using American Standard Code for Information
Interchange (ASCII) code data type conversions.
2. Utilize file handling for reading and writing.
3. Implement flow control for user input and operations.
4. Modularize code with functions for reusability.
Technology to be used:
1. Python SDK 3.9.17
2. Pycharm 2023.1
© Aptech Limited
Sr. Case Studies
No
3 Project 3: Banking System
Scenario:
Develop a Banking Account Management System that should support
following functionalities.
1. Enable users to create new accounts by providing personal details.
2. Validate information and assign unique account numbers.
3. Allow users to check their account balances.
4. Facilitate deposits and withdrawals.
5. Maintain detailed transaction histories, including date, time, and
transaction type.
Hints:
1. Manage accounts and transactions using data structures.
2. Calculate balances and perform transfers with operators.
3. Handle scenarios such as insufficient funds with flow control.
4. Use functions for deposits and withdrawals.
5. Implement iterators for transaction history.
6. Log transactions and generate statements via file handling.
7. Manage errors with exception handling.
8. Ensure data persistence with serialization.
Technology to be used:
1. Python SDK 3.9.17
2. Tkinter
3. MySQL 8.0.33 or higher
4. Pycharm 2023.1
Scenario:
Develop a system for company management that should include
following features.
1. Register employees and track their attendance.
2. Manage leave requests and approvals.
3. Generate reports on attendance, leaves, and overtime.
4. Calculate payroll based on attendance records.
Hints:
1. Manage employee data and leave tracking with various data
structures.
© Aptech Limited
Sr. Case Studies
No
2. Control user input for attendance and leave requests.
3. Organize code with functions for reuse.
4. Store and retrieve data with serialization.
5. Log attendance and generate reports using files operations.
6. Store data in a relational database.
7. Ensure reliability with error handling.
8. Handle multiple requests with threading.
Technology to be used:
1. Python SDK 3.9.17
2. Tkinter
3. MySQL 8.0.33 or higher
4. Pycharm 2023.1
Scenario:
Develop an expense tracker application to help users manage their
finances, that should include features listed as follows:
1. User registration and profile management.
2. Record and categorize expenses.
3. Set budget limits for different categories.
4. Generate reports and visualizations of spending habits.
5. Provide alerts for exceeding budget limits.
Hints:
1. Store expense data with various data structures.
2. Organize code with modular functions and parameters.
3. Use a relational database for data storage.
4. Employ serialization for user and expense data.
5. Implement exception handling for errors.
Technology to be used:
1. Python SDK 3.9.17
2. Tkinter
3. MySQL 8.0.33 or higher
4. Pycharm 2023.1
© Aptech Limited