Advance Python
Advance Python
Introduction to Python
1. Use IDE to develop Programs – Here's a simple example of how you can use an
Integrated Development Environment (IDE) like PyCharm to develop a Python program:
1. Install PyCharm: If you haven't already, download and install PyCharm from the JetBrains
website.
2. Create a New Project: Open PyCharm and create a new project by selecting "File" > "New
Project" from the menu. Choose a location for your project and select a Python interpreter.
3. Create a New Python File: Right-click on your project folder in the Project Explorer pane
and select "New" > "Python File". Give your file a name, such as "hello_world.py".
4. Write Your Python Code: Open the newly created Python file in the editor pane and write
your Python code. For example, you can write a simple "Hello, World!" program:
print("Hello, World!")
5. Run Your Program: To run your program, simply right-click anywhere in the editor pane
and select "Run 'hello_world'". Alternatively, you can use the keyboard shortcut (usually
Ctrl + Shift + F10).
6. View Output: You should see the output of your program in the "Run" tool window at the
bottom of the PyCharm interface.
7. Debugging: PyCharm also provides powerful debugging features. You can set breakpoints
in your code by clicking in the left margin of the editor pane, and then run your program in
debug mode by selecting "Debug 'hello_world'".
8. Explore Features: PyCharm offers many other features to enhance your development
workflow, such as code completion, version control integration, code refactoring, and more.
Take some time to explore the IDE and discover what works best for you.
That's it! You've now developed and run a Python program using PyCharm. Repeat these steps to
create more complex programs and explore the full capabilities of the IDE.
________________________________________________________________________________
2. Basic coding skills - Here are some basic coding skills that are essential for beginners:
1. Understanding Basic Syntax: Learn the syntax rules of the programming language you are
learning (e.g., Python, JavaScript, Java). Understand concepts like variables, data types,
operators, conditionals, loops, functions, and classes.
2. Problem Solving: Practice solving simple problems using your chosen programming
language. Start with small challenges and gradually increase the complexity as you improve.
3. Debugging: Learn how to debug your code when you encounter errors. Use print
statements, debugging tools provided by your IDE, or online debuggers to identify and fix
issues in your code.
4. Reading Documentation: Get comfortable reading documentation for libraries and
frameworks you use. Understand how to find information about functions, classes,
parameters, and usage examples.
5. Version Control: Learn the basics of version control systems like Git. Understand concepts
like repositories, commits, branches, and merges. Practice using Git commands to manage
your codebase.
6. Code Organization: Learn how to organize your code into logical units like functions,
modules, and packages. Follow best practices for naming conventions, commenting, and
documentation.
7. Testing: Understand the importance of testing your code to ensure it behaves as expected.
Learn how to write and run unit tests, integration tests, and other types of tests to verify your
code's correctness.
8. Reading and Writing Files: Learn how to read data from files and write data to files using
your programming language's file I/O capabilities.
9. Understanding Algorithms and Data Structures: Familiarize yourself with common
algorithms and data structures like arrays, linked lists, stacks, queues, sorting algorithms,
and searching algorithms. Understand how to implement and use them in your code.
10.Practice, Practice, Practice: The key to improving your coding skills is practice. Work on
coding challenges, participate in coding competitions, contribute to open-source projects,
and build your own projects to apply what you've learned.
Remember, coding is a skill that takes time and effort to develop. Be patient with yourself, keep
practicing, and don't be afraid to ask for help when you need it. With persistence and dedication,
you'll continue to improve and become a proficient programmer.
________________________________________________________________________________
3. Working with Data Types and variables - Working with data types and
variables is fundamental in programming. Here's a basic overview of how to work with them:
1. Data Types: Programming languages support various data types, such as integers, floats,
strings, booleans, lists, tuples, dictionaries, etc. Each data type represents different kinds of
values and has specific operations associated with it.
2. Variables: Variables are used to store and manipulate data in a program. You can assign
values to variables using the assignment operator (=). Variable names should be descriptive
and follow naming conventions (e.g., start with a letter or underscore, cannot contain spaces,
etc.).
3. Assigning Values: You can assign values to variables using the assignment operator (=). For
example:
# Assigning values to variables
x = 10
name = "John"
is_valid = True
4. Data Type Conversion: You can convert data from one type to another using type
conversion functions (e.g., int(), float(), str()). For example:
# Converting data types
num_str = "10"
num_int = int(num_str)
5. Working with Strings: Strings are sequences of characters enclosed in single or double
quotes. You can perform various operations on strings, such as concatenation, slicing,
formatting, etc.
# Working with strings
name = "John"
greeting = "Hello, " + name + "!"
print(greeting)
# Slicing strings
message = "Hello, World!"
substring = message[7:]
print(substring) # Output: World!
7. Boolean Operations: Boolean data types (True or False) are used to represent logical
values. You can perform boolean operations such as AND, OR, NOT, etc.
# Boolean operations
is_valid = True
is_invalid = not is_valid
8. Lists and Arrays: Lists and arrays are used to store collections of data. You can access,
modify, and manipulate elements in lists using index notation.
# Working with lists
numbers = [1, 2, 3, 4, 5]
first_number = numbers[0]
numbers.append(6)
9. Dictionaries: Dictionaries are collections of key-value pairs. You can access and manipulate
values in dictionaries using keys.
# Working with dictionaries
person = {"name": "John", "age": 30, "city": "New York"}
print(person["name"]) # Output: John
These are just some basic concepts for working with data types and variables in programming. As
you continue learning, you'll encounter more advanced topics and techniques for working with data
in your programs.
________________________________________________________________________________
4. Working with numeric data & string data - Working with numeric data is
essential in programming, especially for performing mathematical calculations and data analysis.
Here are some key concepts and operations related to working with numeric data:
1. Numeric Data Types: Programming languages typically support various numeric data
types, such as integers (int), floating-point numbers (float), and sometimes other specialized
types like complex numbers. Integers represent whole numbers, while floating-point
numbers represent numbers with fractional parts.
2. Arithmetic Operations: You can perform basic arithmetic operations on numeric data
types, including addition, subtraction, multiplication, and division.
# Arithmetic operations
x = 10
y = 5
sum = x + y
difference = x - y
product = x * y
quotient = x / y
3. Integer Division and Modulo: Integer division (//) returns the quotient of a division
operation, discarding any fractional part, while the modulo operator (%) returns the
remainder of a division operation.
# Integer division and modulo
quotient = 10 // 3 # Output: 3
remainder = 10 % 3 # Output: 1
5. Math Module: Most programming languages provide a math module or library with
functions for more advanced mathematical operations, such as trigonometric functions,
logarithms, square roots, etc. These functions need to be imported before use.
# Using math module
import math
6. Numeric Comparisons: You can compare numeric values using comparison operators like
==, !=, <, >, <=, and >=. These operators return boolean values (True or False) based on the
comparison result.
# Numeric comparisons
x = 10
y = 5
is_greater = x > y # Output: True
7. Type Conversion: You can convert numeric values between different data types using type
conversion functions like int(), float(), and str().
# Type conversion
num_str = "10"
num_int = int(num_str)
These are some of the fundamental concepts and operations involved in working with numeric data
in programming. As you progress, you'll encounter more advanced topics and techniques for
handling numeric data in various contexts.
String data - Working with string data is a fundamental aspect of programming, as strings
are used to represent text-based information. Here are some key concepts and operations related to
working with string data:
1. String Data Type: In most programming languages, strings are sequences of characters
enclosed in either single ('') or double ("") quotes. Strings can contain letters, numbers,
symbols, and whitespace.
# String data type
name = "John Doe"
message = 'Hello, World!'
2. String Concatenation: You can concatenate (combine) strings using the + operator.
# String concatenation
greeting = "Hello, " + name + "!"
3. String Indexing and Slicing: Strings can be indexed and sliced to access individual
characters or substrings.
# String indexing and slicing
message = "Hello, World!"
first_char = message[0] # Output: 'H'
substring = message[7:12] # Output: 'World'
4. String Length: You can find the length of a string using the len() function.
# String length
length = len(message) # Output: 13
5. String Methods: Most programming languages provide built-in methods for manipulating
strings, such as converting case, replacing substrings, splitting strings into lists, joining lists
into strings, and more.
# String methods
name_upper = name.upper() # Output: 'JOHN DOE'
message_replace = message.replace('Hello', 'Hi') # Output: 'Hi, World!'
words = message.split(',') # Output: ['Hello', ' World!']
6. String Formatting: String formatting allows you to create formatted strings with
placeholders for variable values. There are various methods for string formatting, including
%-formatting, str.format(), and f-strings (formatted string literals).
# String formatting with f-strings (Python 3.6+)
name = "John"
age = 30
formatted_string = f"My name is {name} and I am {age} years old."
7. Escape Characters: Escape characters are special characters preceded by a backslash () that
represent non-printable characters or special formatting in strings.
# Escape characters
escaped_string = "This is a \"quoted\" string."
newline_string = "This is\na newline."
These are some of the fundamental concepts and operations involved in working with string data in
programming. Strings are versatile and widely used, so mastering string manipulation techniques is
essential for any programmer.
________________________________________________________________________________
5. Python functions - Functions in Python are blocks of reusable code that perform a
specific task. They allow you to break down your code into smaller, more manageable pieces,
making it easier to read, understand, and maintain. Here are the key concepts related to Python
functions:
1. Function Definition: Functions are defined using the def keyword followed by the
function name and parentheses containing optional parameters.
def greet(name):
print(f"Hello, {name}!")
2. Function Call: To execute a function and run its code, you need to call it by using its name
followed by parentheses.
greet("John")
3. Parameters and Arguments: Parameters are placeholders for values that a function expects
to receive, while arguments are the actual values passed to the function during the function
call.
def add(x, y):
return x + y
4. Return Statement: Functions can return a value using the return statement. This value
can then be used in other parts of your code.
def multiply(x, y):
return x * y
5. Default Parameters: You can provide default values for parameters in a function definition.
If a value is not provided during the function call, the default value is used.
def greet(name="World"):
print(f"Hello, {name}!")
7. Variable Number of Arguments: You can define functions that accept a variable number of
arguments using *args and **kwargs parameters.
def sum_values(*args):
return sum(args)
8. Docstrings: Docstrings are used to provide documentation for functions. They are enclosed
in triple quotes and describe what the function does, its parameters, and its return value.
def greet(name):
"""
Greets the user with the given name.
"""
print(f"Hello, {name}!")
9. Scope: Variables defined inside a function have local scope and are only accessible within
that function. Variables defined outside any function have global scope and can be accessed
from anywhere in the code.
def my_function():
x = 10 # Local variable
print(x)
my_function() # Output: 10
10.Lambda Functions: Lambda functions (also known as anonymous functions) are small,
inline functions that can have any number of parameters but only one expression. They are
defined using the lambda keyword.
add = lambda x, y: x + y
result = add(3, 5) # result = 8
These are the basic concepts and features of functions in Python. Functions are powerful tools for
organizing code and making it more modular, reusable, and maintainable.
________________________________________________________________________________
6. Boolean expressions - Boolean expressions in Python are expressions that evaluate to
either True or False. They are commonly used in conditional statements (if, elif, else) and loops to
control the flow of a program based on certain conditions. Here are some key points about boolean
expressions:
1. Comparison Operators: Comparison operators are used to compare values. They include:
• Equal to (==)
• Not equal to (!=)
• Greater than (>)
• Greater than or equal to (>=)
• Less than (<)
• Less than or equal to (<=)
x = 5
y = 10
# Comparison operators
print(x == y) # False
print(x != y) # True
print(x > y) # False
print(x < y) # True
print(x >= y) # False
print(x <= y) # True
2. Logical Operators: Logical operators are used to combine multiple boolean expressions.
They include:
• Logical AND (and)
• Logical OR (or)
• Logical NOT (not)
a = True
b = False
# Logical operators
print(a and b) # False
print(a or b) # True
print(not a) # False
# Membership operators
print(3 in my_list) # True
print(6 not in my_list) # True
4. Identity Operators: Identity operators are used to compare the memory location of two
objects. They include:
• is: Returns True if both variables point to the same object.
• is not: Returns True if both variables do not point to the same object.
x = [1, 2, 3]
y = [1, 2, 3]
# Identity operators
print(x is y) # False
print(x is not y) # True
5. Short-Circuit Evaluation: Python's logical operators (and and or) use short-circuit
evaluation. This means that they stop evaluating expressions as soon as the result is known.
x = 5
y = 10
# Short-circuit evaluation
result = (x > 0) and (y < 5) # Since (x > 0) is False, (y < 5) is not
evaluated
print(result) # False
Boolean expressions are fundamental in programming as they enable you to make decisions and
control the flow of your code based on conditions. Understanding how to use them effectively is
essential for writing efficient and readable code.
________________________________________________________________________________
7. Selection structure - Selection structures, also known as conditional statements, are
used in programming to execute different blocks of code based on certain conditions. In Python, the
main types of selection structures are the if statement, the if-else statement, and the if-elif-else
statement. Here's an overview of each:
1. if Statement: The if statement allows you to execute a block of code only if a specified
condition is true.
x = 10
if x > 5:
print("x is greater than 5")
2. if-else Statement: The if-else statement allows you to execute one block of code if the
condition is true, and another block of code if the condition is false.
x = 3
if x % 2 == 0:
print("x is even")
else:
print("x is odd")
3. if-elif-else Statement: The if-elif-else statement allows you to check multiple conditions
and execute different blocks of code based on the first condition that evaluates to true. The
elif (short for "else if") keyword is used to check additional conditions.
x = 0
if x > 0:
print("x is positive")
elif x < 0:
print("x is negative")
else:
print("x is zero")
4. Nested if Statements: You can also nest if statements within each other to create more
complex conditions.
x = 10
if x > 0:
if x % 2 == 0:
print("x is positive and even")
else:
print("x is positive and odd")
else:
print("x is non-positive")
5. Ternary Conditional Operator: Python also supports a ternary conditional operator, which
provides a compact way to write if-else statements in a single line.
x = 10
result = "Even" if x % 2 == 0 else "Odd"
print(result)
Selection structures are essential for controlling the flow of your program based on different
conditions, allowing your code to make decisions and respond dynamically to changing situations.
Understanding how to use if statements and their variations effectively is fundamental for writing
robust and flexible code.
________________________________________________________________________________
8. Iteration structure - Iteration structures, also known as loops, are used in programming
to execute a block of code repeatedly until a certain condition is met. In Python, there are two main
types of iteration structures: the for loop and the while loop. Here's an overview of each:
1. for Loop: The for loop is used to iterate over a sequence (such as a list, tuple, string, or
range) and execute a block of code for each element in the sequence.
# Iterate over a list
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
2. while Loop: The while loop is used to repeatedly execute a block of code as long as a
specified condition is true. Be cautious, as if the condition never becomes false, it can lead
to an infinite loop.
# Print numbers from 0 to 4 using a whileloop
i = 0
while i < 5:
print(i)
i += 1
3. break Statement: The break statement is used to exit the loop prematurely, regardless of
the loop condition.
# Stop the loop when reaching a certain condition
for i in range(10):
print(i)
if i == 5:
break
4. continue Statement: The continue statement is used to skip the rest of the loop's code
block for the current iteration and proceed to the next iteration.
# Skip printing even numbers
for i in range(10):
if i % 2 == 0:
continue
print(i)
5. else Clause: Both for and while loops can have an optional else clause, which is
executed when the loop completes normally (i.e., without encountering a break statement).
# Find prime numbers
for num in range(2, 10):
for i in range(2, num):
if num % i == 0:
print(f"{num} is not a prime number")
break
else:
print(f"{num} is a prime number")
Iteration structures are fundamental in programming as they allow you to automate repetitive tasks
and process data efficiently. By mastering for and while loops and understanding when to use
them, you can write more concise and powerful code.
________________________________________________________________________________
9. Working with lists - Working with lists in Python is fundamental, as lists are versatile
data structures that can store multiple items in a single variable. Here are some common operations
and techniques for working with lists:
1. Creating Lists: You can create lists by enclosing comma-separated values within square
brackets [].
# Create a list of numbers
numbers = [1, 2, 3, 4, 5]
2. Accessing Elements: You can access individual elements of a list using indexing. Python
uses zero-based indexing, where the first element is at index 0.
print(fruits[0]) # Output: "apple"
print(numbers[2]) # Output: 3
3. Slicing Lists: You can extract a subset of elements from a list using slicing. Slicing syntax is
list[start:stop:step].
print(numbers[1:4]) # Output: [2, 3, 4]
print(fruits[:2]) # Output: ["apple", "banana"]
print(numbers[::2]) # Output: [1, 3, 5]
4. Modifying Lists: Lists are mutable, meaning you can change their elements after they are
created.
fruits[0] = "orange" # Update the first element
numbers.append(6) # Add an element to the end
del fruits[1] # Delete an element
5. List Methods: Python provides built-in methods for performing common operations on
lists, such as adding, removing, sorting, and searching.
numbers.append(6) # Add an element to the end
fruits.insert(1, "banana") # Insert an element at a specific index
fruits.remove("cherry") # Remove a specific element
fruits.sort() # Sort the list in ascending order
index = fruits.index("banana") # Find the index of an element
6. Iterating Over Lists: You can use loops to iterate over the elements of a list.
for fruit in fruits:
print(fruit)
8. Copying Lists: Be careful when copying lists. Using the assignment operator = creates a
reference to the original list, so modifying one list will affect the other. Use slicing or the
copy() method to create a separate copy.
original_list = [1, 2, 3]
copied_list = original_list[:] # or original_list.copy()
Working with lists is essential in Python programming, as they provide a flexible way to store and
manipulate data. By mastering list operations and techniques, you can write more efficient and
expressive code.
________________________________________________________________________________
10. Work with a list of lists - Working with a list of lists in Python allows you to
represent and manipulate two-dimensional data structures, such as matrices or tables. Here's how
you can work with a list of lists:
1. Creating a List of Lists: You can create a list of lists by nesting square brackets []. Each
inner list represents a row, and the outer list contains all the rows.
# Create a list of lists (2D matrix)
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
2. Accessing Elements: You can access individual elements of a list of lists using double
indexing. The first index accesses the row, and the second index accesses the column.
print(matrix[0][1]) # Access element at row 0, column 1 (output: 2)
3. Iterating Over a List of Lists: You can use nested loops to iterate over the elements of a list
of lists.
for row in matrix:
for element in row:
print(element)
4. Modifying Elements: Lists of lists are mutable, so you can modify individual elements as
needed.
matrix[1][1] = 10 # Modify element at row 1, column 1
5. Adding and Removing Rows: You can add or remove entire rows from a list of lists using
list methods.
# Add a new row
new_row = [10, 11, 12]
matrix.append(new_row)
# Remove a row
del matrix[1] # Remove the second row
6. Copying a List of Lists: Be cautious when copying a list of lists, as using the assignment
operator = creates a shallow copy. Modifying one list will affect the other. To create a deep
copy, use list comprehension or the copy module.
# Shallow copy
copied_matrix = matrix[:]
# Deep copy
import copy
copied_matrix = copy.deepcopy(matrix)
Working with a list of lists allows you to represent complex data structures and perform various
operations on them efficiently. By understanding how to manipulate nested lists, you can handle
two-dimensional data effectively in Python.
________________________________________________________________________________
11. Work with tuples - Tuples in Python are similar to lists, but they are immutable,
meaning their elements cannot be changed after creation. Here's how you can work with tuples:
1. Creating Tuples: You can create a tuple by enclosing comma-separated values within
parentheses ().
# Create a tuple of numbers
numbers = (1, 2, 3, 4, 5)
2. Accessing Elements: You can access individual elements of a tuple using indexing. Python
uses zero-based indexing, where the first element is at index 0.
print(fruits[0]) # Output: "apple"
print(numbers[2]) # Output: 3
3. Slicing Tuples: You can extract a subset of elements from a tuple using slicing. Slicing
syntax is tuple[start:stop:step].
print(numbers[1:4]) # Output: (2, 3, 4)
print(fruits[:2]) # Output: ("apple", "banana")
print(numbers[::2]) # Output: (1, 3, 5)
4. Immutable Nature: Tuples are immutable, so you cannot modify their elements or size after
creation. Attempting to do so will result in an error.
# This will raise an error
fruits[0] = "orange"
5. Iterating Over Tuples: You can use loops to iterate over the elements of a tuple.
for fruit in fruits:
print(fruit)
6. Tuple Methods: Tuples have fewer methods compared to lists due to their immutability. The
count() and index() methods are commonly used.
count = numbers.count(3) # Count occurrences of a value
index = fruits.index("banana") # Find the index of a value
7. Tuple Packing and Unpacking: You can pack multiple values into a tuple or unpack a tuple
into multiple variables in a single line.
# Packing
coordinates = (3, 4)
# Unpacking
x, y = coordinates
Tuples are useful for representing fixed collections of items that should not be changed, such as
coordinates, constants, or database records. While they lack some of the flexibility of lists, their
immutability makes them safer and more efficient in certain situations.
________________________________________________________________________________
12. Work with dates and times - Working with dates and times in Python is made easy
by the datetime module. Here's how you can work with dates, times, and timedeltas:
1. Importing the datetime Module: First, import the datetime module to access its
functionality.
import datetime
2. Creating Date and Time Objects: You can create date objects, time objects, or datetime
objects using the constructors provided by the datetime module.
# Create a date object
today = datetime.date.today()
print(today) # Output: YYYY-MM-DD format
formatted_time = current_time.strftime("%H:%M:%S")
print(formatted_time) # Output: HH:MM:SS format
4. Parsing Strings to Dates and Times: Conversely, you can parse strings into date, time, or
datetime objects using the strptime() function and specifying the format.
date_str = "2023-04-25"
parsed_date = datetime.datetime.strptime(date_str, "%Y-%m-%d")
print(parsed_date) # Output: 2023-04-25 00:00:00
5. Working with Timedeltas: Timedeltas represent the difference between two dates or times.
You can perform arithmetic operations with timedeltas.
# Calculate the difference between two dates
delta = datetime.timedelta(days=7)
next_week = today + delta
print(next_week) # Output: Date 7 days from now
Working with dates and times is essential in many applications, such as scheduling tasks, logging
events, or performing time-based calculations. The datetime module provides powerful tools for
handling various aspects of date and time manipulation in Python.
________________________________________________________________________________
13. Get started with dictionaries - Dictionaries in Python are unordered collections of
key-value pairs. They are versatile data structures used to store and retrieve data efficiently. Here's
how you can get started with dictionaries:
1. Creating Dictionaries: You can create dictionaries by enclosing comma-separated key-
value pairs within curly braces {}.
# Create an empty dictionary
my_dict = {}
2. Accessing Elements: You can access the value associated with a specific key using square
brackets [].
print(my_dict["name"]) # Output: "John"
print(my_dict["age"]) # Output: 30
3. Modifying Dictionaries: Dictionaries are mutable, so you can add, update, or remove key-
value pairs as needed.
# Add a new key-value pair
my_dict["gender"] = "Male"
4. Dictionary Methods: Python provides several built-in methods for working with
dictionaries.
# Get a list of all keys
keys = my_dict.keys()
# Get the value for a key, or a default value if the key doesn't exist
value = my_dict.get("name", "Default")
5. Iterating Over Dictionaries: You can iterate over the keys, values, or key-value pairs of a
dictionary using loops.
# Iterate over keys
for key in my_dict:
print(key)
Dictionaries are highly versatile and can be used to represent a wide range of data structures,
including mappings, configurations, and more. By mastering dictionary operations and techniques,
you can write more expressive and efficient Python code.
________________________________________________________________________________
Chapter 2
Classes in Pyhon
1. OOPS concepts -
In Python, object-oriented programming (OOP) is a programming paradigm that revolves around
the concept of "objects," which are instances of classes. OOP allows you to structure your code in a
more organized and modular way by encapsulating data (attributes) and behavior (methods) into
objects.
Here are the key OOP concepts in Python:
1. Class: A class is a blueprint for creating objects. It defines the attributes and methods that
the objects will have. Classes are defined using the class keyword.
2. Object (Instance): An object is an instance of a class. It represents a specific entity with its
own set of attributes and methods.
3. Attributes: Attributes are data associated with objects. They represent the state of the
object. Attributes can be variables that store data.
4. Methods: Methods are functions defined within a class. They define the behavior of the
objects. Methods can operate on the object's attributes and perform actions.
5. Inheritance: Inheritance is a mechanism where a class can inherit attributes and methods
from another class. The class that inherits is called a subclass or derived class, and the class
being inherited from is called a superclass or base class. It promotes code reusability and
establishes a parent-child relationship between classes.
6. Encapsulation: Encapsulation is the bundling of data (attributes) and methods that operate
on the data into a single unit (class). It hides the internal state of the object from the outside
world and only exposes necessary interfaces.
7. Polymorphism: Polymorphism allows objects of different classes to be treated as objects of
a common superclass. It enables methods to be invoked on objects without knowing their
specific types. Polymorphism is achieved through method overriding and method
overloading.
8. Abstraction: Abstraction is the process of hiding the implementation details of a class and
only exposing the necessary features to the user. It focuses on what an object does rather
than how it does it.
Ex. OOP in python
class Animal:
def __init__(self, name):
self.name = name
def sound(self):
pass
class Dog(Animal):
def sound(self):
return "Woof!"
class Cat(Animal):
def sound(self):
return "Meow!"
dog = Dog("Buddy")
print(dog.name) # Output: Buddy
print(dog.sound()) # Output: Woof!
cat = Cat("Whiskers")
print(cat.name) # Output: Whiskers
print(cat.sound()) # Output: Meow!
____________________________________________________________
2. Classes and Objects - In Python, classes and objects are fundamental concepts in
object-oriented programming (OOP). They allow you to create reusable and modular code by
organizing data (attributes) and behavior (methods) into objects.
Here's an overview of classes and objects in Python:
Classes:
• A class is a blueprint or template for creating objects.
• It defines the attributes (data) and methods (functions) that the objects will have.
• Classes are defined using the class keyword followed by the class name.
• Class names are typically written in CamelCase convention (e.g., MyClass).
Syntax
class ClassName:
# Constructor method
def __init__(self, param1, param2, ...):
self.attribute1 = param1
self.attribute2 = param2
...
# Other methods
def method1(self, ...):
# Method body
pass
Objects (Instances):
• An object is an instance of a class.
• It represents a specific entity with its own set of attributes and methods.
• Objects are created by calling the class constructor (the __init__ method).
• Multiple objects can be created from the same class, each with its own unique state.
Syntax
# Define a class
class Dog:
# Constructor method
def __init__(self, name, age):
self.name = name
self.age = age
In this example:
• Dog is a class with attributes name and age, and a method display_info.
• dog1 and dog2 are objects of the Dog class, representing individual dogs with their own
names and ages.
• We can access attributes and call methods of objects using dot notation
(object_name.attribute or object_name.method()).
____________________________________________________________
Syntax:
class ClassName:
def __init__(self, param1, param2, ...):
self.attribute1 = param1
self.attribute2 = param2
...
Example:
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
In this example:
• The Dog class has a constructor method __init__() that initializes the name and age
attributes of the Dog objects.
• When objects dog1 and dog2 are created, the __init__() method is automatically
called with the provided arguments.
• Inside the constructor, the name and age attributes of the objects are initialized using the
parameters passed to the method.
____________________________________________________________
4. Data Hiding - Data hiding in Python refers to the concept of encapsulating data within a
class such that it cannot be accessed directly from outside the class. This is achieved by prefixing
the attribute names with double underscores (__), making them private to the class. This practice is
also known as "name mangling."
Here's how data hiding works in Python:
1. Private Attributes: Attributes that are prefixed with double underscores (__) are considered
private to the class. They cannot be accessed directly from outside the class.
2. Name Mangling: When you prefix an attribute with double underscores, Python internally
changes its name to include the class name as a prefix. This is done to prevent name clashes
in subclasses.
3. Accessing Private Attributes: Although private attributes cannot be accessed directly from
outside the class, they can still be accessed indirectly using methods defined within the class
(e.g., getter and setter methods).
4. Getter and Setter Methods: Getter methods are used to access private attributes, and setter
methods are used to modify them. These methods provide controlled access to the private
data and allow for validation or additional logic to be applied.
Here's an example demonstrating data hiding in Python:
class MyClass:
def __init__(self):
self.__private_attr = 10 # Private attribute
def get_private_attr(self):
return self.__private_attr # Getter method
def set_private_attr(self, value):
if value >= 0:
self.__private_attr = value # Setter method
In this example, __private_attr is a private attribute of the MyClass class. It can only be
accessed and modified using the get_private_attr() and set_private_attr()
methods, respectively. This encapsulation prevents direct access to the attribute from outside the
class, providing better control over the class's data.
____________________________________________________________
5. Instance Method - Instance methods in Python are functions defined within a class that
operate on instances of the class. These methods are associated with objects (instances) of the class
and can access and modify the object's attributes.
Here are the key characteristics of instance methods:
1. Defined within a Class: Instance methods are defined within the body of a class using the
def keyword.
2. Access Instance Attributes: Instance methods have access to the attributes of the object
(instance) they are called on using the self parameter.
3. Can Modify Object State: Instance methods can modify the state of the object by changing
the values of its attributes.
4. Useful for Encapsulation and Abstraction: Instance methods are commonly used to
encapsulate behavior associated with objects and implement the logic specific to the class.
5. Implicitly Pass self Parameter: When calling an instance method, Python automatically
passes the object itself as the first argument (self). This allows the method to access the
object's attributes and other methods.
Here's a simple example demonstrating instance methods in Python:
class Car:
def __init__(self, make, model):
self.make = make
self.model = model
self.odometer = 0
def display_odometer(self):
print(f"Odometer reading: {self.odometer} miles")
# Create an object of the Car class
my_car = Car("Toyota", "Camry")
In this example:
• drive() and display_odometer() are instance methods of the Car class.
• drive() method increments the odometer attribute by the specified distance.
• display_odometer() method prints the current odometer reading of the car.
• Instance methods are called on the my_car object, which operates on the attributes of that
specific car instance.
____________________________________________________________
6. Special Methods - Special methods in Python, also known as magic methods or dunder
methods, are predefined methods with double underscores (__) at the beginning and end of their
names. These methods allow classes to emulate built-in Python behavior and enable customization
of object behavior in various contexts.
Here are some key points about special methods:
1. Automatic Invocation: Special methods are invoked implicitly by Python in response to
certain operations or built-in functions. For example, when you add two objects together
using the + operator, Python calls the __add__() method of the left operand.
5. Customizing Behavior: By implementing special methods in your classes, you can make
your objects behave like built-in types or define custom behavior for specific operations.
Here's an example demonstrating the use of a special method:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
In this example:
• The Vector class defines the __add__() special method to customize the behavior of
the + operator for adding vectors.
• When v1 + v2 is executed, Python calls the __add__() method of the v1 object with
v2 as the argument.
• The __add__() method returns a new Vector object representing the sum of the two
vectors.
____________________________________________________________
7. Class Variables - Class variables in Python are variables that are shared among all
instances (objects) of a class. They are defined within the class but outside of any instance methods.
Class variables are accessed using the class name rather than through instances of the class.
Here are some key points about class variables:
1. Defined at Class Level: Class variables are declared at the top level of a class definition,
outside of any instance methods. They are typically initialized within the class body.
2. Shared Among Instances: Class variables are shared among all instances of the class. This
means that changes made to a class variable by one instance will affect all other instances.
3. Accessed via Class Name: Class variables are accessed using the class name followed by
the dot operator (.). They can also be accessed within instance methods using the self
parameter or directly using the class name.
4. Useful for Storing Shared Data: Class variables are useful for storing data that is shared
among all instances of a class, such as configuration settings, constants, or shared state.
5. Can be Accessed and Modified: Class variables can be accessed and modified both inside
and outside the class. However, it's important to be aware of the implications of modifying
class variables, as changes will affect all instances of the class.
Here's an example demonstrating the use of class variables in Python:
class Employee:
# Class variable
num_employees = 0
def display_info(self):
print(f"Name: {self.name}, Salary: {self.salary}")
In this example:
• num_employees is a class variable that tracks the total number of employees.
• Every time an instance of the Employee class is created, the num_employees class
variable is incremented.
• Class variables can be accessed and modified using either the class name
(Employee.num_employees) or an instance of the class (emp1.num_employees).
____________________________________________________________
def speak(self):
return "Unknown sound"
In this example:
• Animal is the parent class with an speak() method.
• Dog and Cat are child classes that inherit from Animal.
• Each child class overrides the speak() method with its own implementation.
• Instances of Dog and Cat can call the speak() method, which provides the appropriate
sound for each animal.
____________________________________________________________
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
# Function that takes an Animal object and calls its speak method
def make_sound(animal):
return animal.speak()
In this example:
• The Animal class defines a speak() method, which is overridden in the Dog and Cat
subclasses.
• The make_sound() function takes an Animal object as an argument and calls its
speak() method.
• Even though make_sound() is called with objects of different subclasses (Dog and Cat),
it operates polymorphically by invoking the appropriate speak() method for each object.
____________________________________________________________
In Python, you can identify the type of an object using several methods. Here are some common
techniques:
1. Using the type() Function: The type() function returns the type of an object. It takes a
single argument and returns the type object.
x = 5
print(type(x)) # Output: <class 'int'>
• Using the isinstance() Function: The isinstance() function checks if an object
is an instance of a specified class or any of its subclasses. It takes two arguments: the object
and the class (or tuple of classes) to check against.
x = 5
print(isinstance(x, int)) # Output: True
• Using Comparison Operators: You can also use comparison operators to check the type of
an object.
x = 5
print(type(x) == int) # Output: True
• Using the __class__ Attribute: Every object in Python has a __class__ attribute that
holds a reference to its class.
x = 5
print(x.__class__) # Output: <class 'int'>
• Using type with Inheritance: You can also use type to check if an object is an instance
of a class or any of its subclasses.
class A:
pass
class B(A):
pass
x = B()
print(type(x) is A) # Output: False
print(type(x) is B) # Output: True
Choose the method that best suits your needs based on the context and requirements of your code.
Each method has its advantages and use cases.
____________________________________________________________
11. Custom Exception Classes - Custom exception classes in Python allow you to
define your own exception types to handle specific error conditions in your code. By creating
custom exception classes, you can make your code more expressive, readable, and maintainable.
Here's how you can create custom exception classes in Python:
1. Subclass Exception: To create a custom exception class, you typically subclass the built-
in Exception class or one of its subclasses, such as ValueError, TypeError, etc.
2. Define Your Exception: Define your custom exception class by providing a name for the
class and optionally adding additional attributes or methods as needed.
3. Raise Your Custom Exception: In your code, raise an instance of your custom exception
class when a specific error condition occurs.
Here's a simple example of a custom exception class:
class CustomError(Exception):
def __init__(self, message="A custom error occurred"):
self.message = message
super().__init__(self.message)
# Example usage
def divide(x, y):
if y == 0:
raise CustomError("Cannot divide by zero")
return x / y
try:
result = divide(10, 0)
except CustomError as e:
print("Error:", e.message)
In this example:
• We define a custom exception class CustomError that inherits from the built-in
Exception class.
• The __init__() method initializes the exception object with an optional error message.
• Inside the divide() function, if the divisor y is zero, we raise an instance of
CustomError with a specific error message.
• In the try block, we call the divide() function and handle the raised exception by
printing the error message.
Custom exception classes are useful for organizing and handling errors in a structured way,
especially when you need to differentiate between different types of errors in your code. They allow
you to provide more context and information about the error condition, making it easier to debug
and troubleshoot issues.
____________________________________________________________
12. Iterators - Iterators in Python are objects that implement the iterator protocol, which
consists of two methods: __iter__() and __next__(). Iterators are used to iterate over a
sequence of elements, one element at a time, and they maintain internal state to keep track of the
current position within the sequence.
Here's a brief overview of iterators in Python:
1. __iter__() Method: This method returns the iterator object itself. It is called when the
iterator is initialized or reset for iteration.
2. __next__() Method: This method returns the next element in the sequence. It raises a
StopIteration exception when there are no more elements to return.
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.data):
raise StopIteration
else:
value = self.data[self.index]
self.index += 1
return value
In this example:
• MyIterator is a custom iterator class that iterates over a list of integers.
• The __iter__() method returns the iterator object itself.
• The __next__() method retrieves the next element from the list and updates the internal
index. If there are no more elements, it raises a StopIteration exception.
• The for loop iterates over the my_iterator object, repeatedly calling its __next__()
method to retrieve elements until the iterator is exhausted.
____________________________________________________________
13. Generators - Generators in Python are a convenient way to create iterators. They are
implemented using a special kind of function called a generator function, which uses the yield
keyword to yield values one at a time instead of returning them all at once. This allows you to
iterate over a potentially infinite sequence of values without consuming excessive memory.
Here's a summary of generators in Python:
1. Generator Function: A generator function is defined like a regular function, but it contains
one or more yield statements instead of a return statement. When called, a generator
function returns a generator object, which is an iterator.
2. yield Statement: The yield statement is used inside a generator function to yield a
value to the caller. When the generator function is called, execution is suspended at the
yield statement, and the yielded value is returned. The generator function retains its state,
allowing it to resume execution from where it left off when called again.
3. Iteration: Generators can be iterated over using a for loop, just like other iterable objects.
Each iteration calls the generator function's body until a yield statement is encountered, at
which point control is returned to the caller with the yielded value.
4. Lazy Evaluation: Generators produce values lazily, meaning they generate values on
demand as they are iterated over. This allows them to efficiently handle large or infinite
sequences without precomputing all values upfront.
5. Memory Efficiency: Because generators produce values on-the-fly and do not store the
entire sequence in memory, they are memory efficient, especially when dealing with large
data sets or infinite sequences.
Here's an example of a generator function that generates Fibonacci numbers:
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
In this example:
• The fibonacci() function is a generator function that yields Fibonacci numbers
indefinitely.
• We create a generator object fib_gen by calling the fibonacci() function.
• We use a for loop to iterate over the generator object and print the first 10 Fibonacci
numbers. Each call to next(fib_gen) advances the generator to the next yield statement,
producing the next Fibonacci number.
____________________________________________________________
14. Decorators - Decorators are a powerful feature in Python that allow you to modify or
extend the behavior of functions or methods without changing their source code. Decorators are
implemented using functions and provide a clean and concise way to add functionality to existing
code.
Here are the key points about decorators in Python:
1. Function Decorators: Decorators are implemented as regular Python functions that take
another function as an argument and return a new function. They are typically used with the
@decorator_name syntax, placed above the function definition.
@logger
def add(a, b):
return a + b
In this example:
• The logger() function is a decorator that takes another function func as its argument.
• Inside logger(), a new function wrapper is defined, which adds logging functionality
around the original function func.
• The wrapper function calls the original function func with the provided arguments and
keyword arguments.
• The @logger decorator is applied to the add() function, which means that calls to
add() will be intercepted and logged by the logger() decorator.
____________________________________________________________
Chapter 3
I/O and Error Handling
1. Introduction -
In Python, input/output (I/O) operations and error handling are crucial aspects of programming.
Here's a brief overview of both:
Input/Output (I/O):
1. Printing Output: The print() function is used to display output on the console. You can
print strings, variables, and expressions.
print("Hello, world!")
• Reading Input: The input() function is used to accept input from the user. It returns the
user's input as a string.
name = input("Enter your name: ")
• File Handling: Python provides built-in functions and modules for file I/O operations. The
open() function is used to open a file. Use modes like 'r' for reading, 'w' for writing,
and 'a' for appending.
# Reading from a file
with open("filename.txt", "r") as file:
data = file.read()
# Writing to a file
with open("output.txt", "w") as file:
file.write("Hello, world!")
Error Handling:
Python uses the try, except, finally, and else blocks for error handling. This allows you to
handle exceptions gracefully.
1. try-except block: Used to catch exceptions. Code within the try block is executed, and if
any exception occurs, it is caught by the except block.
try:
# Code that may raise an exception
result = 10 / 0
except ZeroDivisionError:
print("Error: Division by zero!")
• finally block: Optional block executed regardless of whether an exception occurred or not.
It's typically used for cleanup actions.
try:
# Code that may raise an exception
result = 10 / 0
except ZeroDivisionError:
print("Error: Division by zero!")
finally:
print("Execution complete.")
• else block: Executed if the code in the try block doesn’t raise any exceptions.
try:
# Code that may raise an exception
result = 10 / 2
except ZeroDivisionError:
print("Error: Division by zero!")
else:
print("Result:", result)
Error handling allows your program to handle unexpected situations without crashing and provides
mechanisms for graceful recovery or appropriate error messages.
____________________________________________________________
2. Data Streams - In Python, data streams are a way of handling continuous flows of data,
whether from input sources like files, network sockets, or output destinations like files, network
connections, or standard output. Python provides several modules and functions to work with data
streams efficiently. Here's an overview:
1. File Streams:
Python's file I/O functionality allows reading from and writing to files, which are a common form
of data streams.
• Reading from Files:
with open("data.txt", "r") as file:
for line in file:
print(line)
• Writing to Files:
with open("output.txt", "w") as file:
file.write("Hello, world!")
2. Standard Streams:
Python also provides access to the standard input, output, and error streams through sys.stdin,
sys.stdout, and sys.stderr, respectively, in the sys module.
• Standard Input:
import sys
• Standard Output:
import sys
sys.stdout.write("Hello, world!\n")
3. Network Streams:
Python's socket module enables communication over networks, allowing you to read from and
write to network connections.
• Socket for TCP Client:
import socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect(("example.com", 80))
data = client_socket.recv(1024)
print("Received:", data.decode())
client_socket.close()
while True:
client_socket, address = server_socket.accept()
data = client_socket.recv(1024)
print("Received:", data.decode())
client_socket.close()
These are just a few examples of how Python handles data streams. Depending on your
requirements, you might also explore libraries like io for more advanced stream processing or
third-party libraries for specific needs, such as requests for HTTP requests or pandas for data
manipulation.
____________________________________________________________
3. Creating your own Data Stream -Creating your own data stream involves defining
a class that implements the stream interface. Here's a simple example of how you can create a
custom data stream class in Python:
class DataStream:
def __init__(self, data):
self.data = data
self.index = 0
def read(self):
if self.index >= len(self.data):
return None
else:
value = self.data[self.index]
self.index += 1
return value
In this example:
• The DataStream class has an internal data list that stores the data.
• The read method reads data from the stream sequentially.
• The write method appends data to the stream.
You can customize this class according to your specific needs, such as adding error handling,
buffering, or implementing additional methods for seeking, flushing, or closing the stream.
Creating your own data stream allows you to abstract away the complexity of handling data from
various sources or to simulate streams for testing purposes. Depending on your requirements, you
might extend this example to support more advanced features or integrate it with other parts of your
application.
____________________________________________________________
4. Access Modes - In Python, access modes are used when opening files to specify the
operations that can be performed on the file. These modes determine whether the file is opened for
reading, writing, or both, as well as whether the file should be created if it doesn't exist or truncated
if it does. Here are the commonly used access modes:
1. Read Mode ('r'):
• Opens the file for reading.
• The file pointer is placed at the beginning of the file.
• If the file does not exist, it raises a FileNotFoundError.
with open("example.txt", "r") as file:
data = file.read()
These are the basic access modes in Python for file I/O operations. Depending on your
requirements, you can choose the appropriate mode to perform read, write, or read-write operations
on files.
____________________________________________________________
5. Writing Data To a File -Writing data to a file in Python involves opening a file in write
mode and then using methods to write data to it. Here's a basic example:
python
In this example:
• The open() function is used to open a file named "output.txt" in write mode ("w"). If the
file doesn't exist, it will be created. If it does exist, its contents will be overwritten.
• The with statement ensures that the file is properly closed after writing, even if an
exception occurs during the writing process.
• The write() method is used to write data to the file. You can pass a string to this method
to write it to the file.
After executing this code, a file named "output.txt" will be created (or overwritten if it already
exists) in the current directory with the following contents:
Hello, world!
This is a new line.
You can write any data to the file using the write() method, including strings, numbers, or even
the contents of other files. Just make sure to convert non-string data types to strings before writing
them to the file.
____________________________________________________________
6. Reding Data From a File - Reading data from a file in Python involves opening the
file in read mode and then using methods to read data from it. Here's a basic example:
# Open a file in read mode ('r')
with open("input.txt", "r") as file:
# Read data from the file
data = file.read()
print(data)
In this example:
• The open() function is used to open a file named "input.txt" in read mode ("r").
• The with statement ensures that the file is properly closed after reading, even if an
exception occurs during the reading process.
• The read() method is used to read the entire contents of the file as a single string and store
it in the variable data.
After executing this code, the contents of the file "input.txt" will be printed to the console.
You can also read the file line by line using a loop:
# Open a file in read mode ('r')
with open("input.txt", "r") as file:
# Read data line by line
for line in file:
print(line.strip()) # strip() removes leading and trailing whitespace
and newline characters
This will print each line of the file "input.txt" to the console, removing leading and trailing
whitespace and newline characters using the strip() method.
You can also use methods like readline() to read a single line at a time, or readlines() to
read all lines into a list. Choose the method that best fits your specific use case.
____________________________________________________________
• flush(): Flushes the internal buffer, ensuring that all buffered data is written to the file.
with open("output.txt", "w") as file:
file.write("Hello, world!")
file.flush()
• close(): Closes the file. Though not strictly necessary when using the with statement
(as it automatically closes the file), it's useful when working with file objects outside of a
with context.
file = open("input.txt", "r")
data = file.read()
file.close()
These methods provide you with powerful capabilities to manipulate files in various ways, from
writing and reading data to controlling the file pointer and managing buffers.
____________________________________________________________
8. Handling IO Exceptions - Handling I/O exceptions is essential for robust file
handling in Python. File operations can fail due to various reasons, such as a file not existing,
insufficient permissions, or disk space issues. Python provides mechanisms to catch and handle
these exceptions gracefully using try-except blocks. Here's how you can handle I/O exceptions
effectively:
try:
# Open the file for reading
with open("input.txt", "r") as file:
# Read data from the file
data = file.read()
print(data)
except FileNotFoundError:
print("File not found!")
except PermissionError:
print("Permission denied!")
except IOError as e:
print("I/O error:", e)
except Exception as e:
print("An unexpected error occurred:", e)
In this example:
• We attempt to open a file named "input.txt" for reading within a try block.
• Inside the try block, we read data from the file.
• If any exceptions occur during file operations, Python checks each except block to see if it
matches the type of exception raised. If a match is found, the corresponding block is
executed.
• The FileNotFoundError exception is raised when the specified file does not exist,
while PermissionError is raised when there are insufficient permissions to perform the
file operation.
• The IOError is a generic exception for I/O-related errors. We can catch it to handle any
other I/O errors not explicitly handled by more specific exception types.
• The Exception block catches any other unexpected exceptions that might occur.
• In each except block, we print an appropriate error message or handle the exception in a
way that's suitable for our application.
By handling I/O exceptions, you can make your code more robust and resilient to unexpected
situations when working with files.
____________________________________________________________
9. Errors - Errors in programming, including in Python, can broadly be categorized into two
main types: syntax errors and exceptions.
1. Syntax Errors: Syntax errors, also known as parsing errors, occur when the code violates
the syntax rules of the programming language. These errors are detected by the Python
interpreter during the parsing phase before the code is executed. Common examples include
missing colons, mismatched parentheses, or misspelled keywords.
• # Syntax Error: Missing colon
if x == 5
print("x is equal to 5")
When you run code with a syntax error, Python will raise a SyntaxError and provide
information about the error, including the location of the syntax violation.
• Exceptions: Exceptions, on the other hand, occur during the execution of a program when
something unexpected happens. These can include errors like division by zero, attempting to
access an index that doesn't exist in a list, or attempting to open a file that doesn't exist.
Exceptions disrupt the normal flow of the program unless they are handled appropriately.
# ZeroDivisionError: Division by zero
result = 10 / 0
To handle exceptions, you can use try, except, finally, and else blocks to gracefully
recover from errors or provide appropriate error messages to users.
2. try:
result = 10 / 0
except ZeroDivisionError:
print("Error: Division by zero!")
In this example, if a ZeroDivisionError occurs during the division operation, the code
inside the except block is executed, preventing the program from crashing.
Understanding and handling errors effectively is essential for writing robust and reliable code.
Python's error handling mechanisms provide ways to identify, anticipate, and recover from errors,
ensuring smoother execution of programs.
____________________________________________________________
10. Run Time Errors - Runtime errors, also known as exceptions, occur during the
execution of a program. Unlike syntax errors, which are caught by the Python interpreter during the
parsing phase, runtime errors happen while the program is running. These errors can occur due to
various reasons, such as invalid input, division by zero, attempting to access an index out of range,
or using a variable that hasn't been initialized.
Here are some common types of runtime errors in Python:
1. ZeroDivisionError: Occurs when dividing a number by zero.
result = 10 / 0
• IndexError: Occurs when trying to access an index that is out of range for a sequence (e.g.,
list, tuple).
my_list = [1, 2, 3]
print(my_list[3])
• KeyError: Occurs when trying to access a dictionary key that does not exist.
my_dict = {"a": 1, "b": 2}
print(my_dict["c"])
• ValueError: Occurs when a built-in operation or function receives an argument with the
right type but an inappropriate value.
num = int("abc")
• FileNotFoundError: Occurs when trying to open a file that does not exist.
6. with open("nonexistent_file.txt", "r") as file:
data = file.read()
To handle runtime errors, you can use try, except, finally, and else blocks to gracefully
recover from errors or provide appropriate error messages to users. Proper error handling helps
ensure that your program can handle unexpected situations and continues to execute smoothly.
____________________________________________________________
11. The Exception Model - The exception model in Python is a mechanism for handling
runtime errors (exceptions) that occur during the execution of a program. It provides a way to detect
and respond to exceptional conditions that may arise during program execution. Here's an overview
of the key components of the exception model in Python:
1. Exceptions: Exceptions are objects representing errors or exceptional conditions that occur
during program execution. Python provides a variety of built-in exception classes for
common types of errors, such as ZeroDivisionError, TypeError, ValueError,
FileNotFoundError, and more. Additionally, you can define your own custom
exception classes by subclassing Exception or one of its subclasses.
2. try-except Block: The try statement is used to wrap the code that may raise an exception.
It allows you to catch and handle exceptions gracefully. The except block specifies the
type of exception to catch and provides the code to handle the exception.
• try:
# Code that may raise an exception
result = 10 / 0
except ZeroDivisionError:
# Code to handle the ZeroDivisionError exception
print("Error: Division by zero!")
• Multiple Except Blocks: You can have multiple except blocks to handle different types
of exceptions. Python will execute the code in the first matching except block and ignore
subsequent blocks.
• try:
# Code that may raise an exception
result = 10 / 0
except ZeroDivisionError:
# Code to handle the ZeroDivisionError exception
print("Error: Division by zero!")
except ValueError:
# Code to handle the ValueError exception
print("Error: Invalid input!")
• else Block: The else block is executed if no exceptions occur within the try block. It is
typically used to execute code that should run only if no exceptions were raised.
• try:
# Code that may raise an exception
result = 10 / 2
except ZeroDivisionError:
# Code to handle the ZeroDivisionError exception
print("Error: Division by zero!")
else:
# Code to execute if no exceptions occurred
print("Result:", result)
• finally Block: The finally block is always executed regardless of whether an exception
occurred or not. It is commonly used for cleanup actions, such as closing files or releasing
resources.
• try:
# Code that may raise an exception
file = open("example.txt", "r")
data = file.read()
except FileNotFoundError:
# Code to handle the FileNotFoundError exception
print("Error: File not found!")
finally:
# Code to execute for cleanup
if file:
file.close()
• Raising Exceptions: You can explicitly raise exceptions using the raise statement. This
allows you to signal that an error condition has occurred in your code.
if x < 0:
raise ValueError("x must be non-negative")
1. Exception Hierarchy: Python's built-in exceptions are organized into a hierarchy, with the
base class being BaseException. This hierarchy allows you to catch more general
exceptions or specific ones, depending on your needs.
Overall, the exception model in Python provides a powerful and flexible way to handle errors and
exceptional conditions in your code, improving its robustness and reliability.
____________________________________________________________
These are just some of the main categories in the Python exception hierarchy. Each exception class
typically provides information about a specific type of error, allowing for more precise error
handling in your code. Understanding the exception hierarchy can help you choose the appropriate
exception to catch and handle in different situations.
____________________________________________________________
try:
# Code that may raise exceptions
result = 10 / 0
except ZeroDivisionError:
# Handle division by zero error
print("Error: Division by zero!")
except TypeError:
# Handle type error
print("Error: Invalid operation!")
except Exception as e:
# Handle other exceptions
print("An unexpected error occurred:", e)
In this example:
• We have a try block containing the code that may raise exceptions.
• We use multiple except blocks, each targeting a specific type of exception
(ZeroDivisionError, TypeError, and a generic Exception).
• If a ZeroDivisionError occurs (division by zero), the code inside the corresponding
except block is executed.
• If a TypeError occurs (invalid operation), the code inside its except block is executed.
• The generic except Exception as e block catches any other exceptions not
explicitly handled by the previous except blocks. It prints an error message along with the
exception details.
You can also use a single except block to catch multiple types of exceptions by specifying them
as a tuple:
try:
# Code that may raise exceptions
result = int("abc")
except (ValueError, TypeError):
# Handle value and type errors
print("Error: Invalid input!")
This approach is useful when you want to handle different types of exceptions in the same way.
When handling multiple exceptions, it's essential to consider the order of the except blocks.
Python checks each except block in order, and the first matching block is executed. Therefore, if
you have a generic except block at the end, make sure to place more specific exception handlers
before it to avoid catching exceptions unintentionally.
____________________________________________________________
14. Working with Directories - Working with directories in Python is commonly done
using the os module, which provides various functions for interacting with the operating system.
Here's how you can perform common directory-related operations:
1. Creating a Directory:
You can create a new directory using the os.mkdir() function.
import os
5. Removing a Directory:
You can remove a directory using the os.rmdir() function.
import os
# Remove a directory
os.rmdir("new_directory")
These are some of the basic operations you can perform when working with directories in Python.
The os module provides many more functions for directory manipulation and path handling,
allowing you to interact with the file system in a platform-independent way.
____________________________________________________________
Chapter 4
An Introduction to relational database
5. UPSERT (or MERGE): Inserts a new record if it does not exist or updates the existing
record if it does.
-- PostgreSQL
INSERT INTO table_name (column1, column2) VALUES (value1, value2)
ON CONFLICT (column_unique_constraint) DO UPDATE SET column1 = value1, column2 =
value2;
-- MySQL
INSERT INTO table_name (column1, column2) VALUES (value1, value2)
ON DUPLICATE KEY UPDATE column1 = value1, column2 = value2;
7. ALTER TABLE: Modifies the structure of a table (e.g., adding, modifying, or dropping
columns).
ALTER TABLE table_name ADD column_name datatype;
ALTER TABLE table_name MODIFY column_name datatype;
ALTER TABLE table_name DROP COLUMN column_name;
8. CREATE TABLE: Creates a new table with specified columns and constraints.
CREATE TABLE table_name (
column1 datatype,
column2 datatype,
...
);
These are some of the fundamental SQL statements for data manipulation. Depending on the
database management system (DBMS) you are using (e.g., MySQL, PostgreSQL, SQL Server,
Oracle), the syntax and features may vary slightly.
____________________________________________________________
____________________________________________________________
2. Connect to the Database: Use the library's functions or methods to connect to the database.
For SQLite, you can use the sqlite3 module from the Python standard library to connect
to an SQLite database file.
python
import sqlite3
3. Execute SQL Queries: Use the cursor object to execute SQL queries on the database. You
can execute any SQL statement supported by the database, such as SELECT, INSERT,
UPDATE, DELETE, CREATE TABLE, etc.
# Execute SQL queries
cursor.execute('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name
TEXT, age INTEGER)')
cursor.execute('INSERT INTO users (name, age) VALUES (?, ?)', ('John', 30))
conn.commit() # Commit the transaction
4. Fetch Data: If your SQL query returns data (e.g., SELECT queries), you can fetch the
results using methods like fetchone(), fetchall(), or fetchmany().
# Fetch data
cursor.execute('SELECT * FROM users WHERE age > ?', (25,))
rows = cursor.fetchall()
for row in rows:
print(row)
5. Close the Connection: Once you're done working with the database, don't forget to close
the connection to release resources.
# Close the connection
conn.close()
This is just a basic example of how you can work with a database in Python. Depending on your
specific use case and the complexity of your application, you may need to handle transactions,
handle errors, use ORM (Object-Relational Mapping) libraries like SQLAlchemy, and more.
____________________________________________________________
def handle_event():
# Function to handle the button click event
print("Button clicked!")
In this example:
• We import the tkinter module and define a function handle_event() to handle the
event when the button is clicked.
• We create the main application window using tk.Tk() and set its title.
• We create a button widget using tk.Button() with the text "Click Me" and specify the
command parameter to call the handle_event() function when the button is clicked.
• We use the pack() method to add the button to the window.
• Finally, we start the Tkinter event loop using root.mainloop(), which waits for events
(such as button clicks) and handles them accordingly.
When you run this code, a window will appear with a button labeled "Click Me". When you click
the button, the message "Button clicked!" will be printed in the console or terminal.
This is a basic example, and you can expand upon it to create more complex GUI applications with
multiple widgets and event handlers.
____________________________________________________________
import tkinter as tk
def handle_button_click():
# Function to handle button click event
label.config(text="Button Clicked!")
In this example:
• We import the tkinter module as tk.
• We define a function handle_button_click() to handle the event when the button is
clicked. In this case, it updates the text of the label.
• We create the main application window using tk.Tk() and set its title.
• We create components such as a label (tk.Label) and a button (tk.Button), and
configure them as needed.
• We add the components to the window using the pack() method, which organizes them in
a vertical layout.
• Finally, we start the Tkinter event loop using root.mainloop(), which waits for events
(such as button clicks) and handles them accordingly.
You can work with various other components such as text entry fields, check buttons, radio buttons,
list boxes, scroll bars, and more, depending on your application's requirements. Additionally, you
can customize the appearance and behavior of these components by configuring their properties and
attaching event handlers as needed.
____________________________________________________________
Chapter 5
Implement Machine Learning Algorithm
1. Usage of Numpy for numerical Data - NumPy is a powerful Python library for
numerical computing that provides support for large, multi-dimensional arrays and matrices, along
with a collection of mathematical functions to operate on these arrays efficiently. Here's a brief
overview of how you can use NumPy for numerical data manipulation:
1. Import NumPy: Begin by importing the NumPy library.
import numpy as np
2. Creating NumPy Arrays: You can create NumPy arrays using lists or tuples.
# 1D array
arr1 = np.array([1, 2, 3, 4, 5])
# 2D array
arr2 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
3. Array Attributes: NumPy arrays have various attributes such as shape, size, and data type.
print(arr1.shape) # (5,)
print(arr2.shape) # (3, 3)
print(arr2.size) # 9
print(arr2.dtype) # int64
4. Array Operations: NumPy provides a wide range of mathematical operations for arrays.
# Element-wise addition
result = arr1 + arr1
# Element-wise multiplication
result = arr1 * arr1
# Matrix multiplication
result = np.dot(arr2, arr2)
5. Indexing and Slicing: You can access elements of a NumPy array using indexing and
slicing.
# Accessing elements
print(arr1[0]) # 1
print(arr2[0, 1]) # 2
# Slicing
print(arr1[1:4]) # [2, 3, 4]
print(arr2[:, 1:]) # [[2, 3], [5, 6], [8, 9]]
6. Array Manipulation: NumPy provides functions for array manipulation, such as reshaping,
stacking, and splitting.
# Reshaping
arr3 = arr1.reshape((5, 1))
# Stacking
result = np.hstack((arr1, arr1))
result = np.vstack((arr2, arr2))
# Splitting
arr4, arr5 = np.split(arr2, [2])
# Statistical functions
result = np.mean(arr1)
result = np.std(arr1)
These are just a few examples of what you can do with NumPy for numerical data manipulation.
NumPy's extensive functionality and performance make it an essential tool for scientific computing
and data analysis in Python.
____________________________________________________________
2. Usage of Pandas for Data Analysis - Pandas is a powerful Python library for data
analysis that provides data structures and functions designed to make working with structured data
easy and intuitive. Here's an overview of how you can use Pandas for data analysis:
1. Import Pandas: Begin by importing the Pandas library.
import pandas as pd
2. Loading Data: Pandas provides functions to read data from various file formats such as
CSV, Excel, SQL databases, and more.
# Read data from a CSV file
df = pd.read_csv('data.csv')
3. Exploring Data: Pandas provides functions to explore and understand the structure of the
data.
# Display the first few rows of the DataFrame
print(df.head())
4. Indexing and Selecting Data: Pandas allows you to select subsets of data using indexing
and slicing.
# Select a single column
column = df['column_name']
5. Data Cleaning and Preparation: Pandas provides functions for data cleaning and
preparation, such as handling missing values, removing duplicates, and transforming data.
# Handle missing values
df.dropna() # Drop rows with missing values
df.fillna(value) # Fill missing values with a specific value
# Remove duplicates
df.drop_duplicates()
# Data transformation
df['new_column'] = df['column'].apply(lambda x: x * 2)
6. Data Aggregation and Grouping: Pandas allows you to aggregate and group data for
analysis.
# Group data by a column and compute aggregate statistics
grouped = df.groupby('column').agg({'column2': 'mean'})
# Pivot table
pivot_table = df.pivot_table(index='column1', columns='column2', values='value',
aggfunc='mean')
7. Data Visualization: Pandas integrates with Matplotlib and other visualization libraries to
create plots and charts.
# Plot data
df.plot(x='column1', y='column2', kind='scatter')
# Histogram
df['column'].plot(kind='hist')
These are just a few examples of what you can do with Pandas for data analysis. Pandas' rich
functionality and ease of use make it a popular choice for data manipulation and analysis in Python.
____________________________________________________________
2. Creating Basic Plots: Matplotlib provides functions to create various types of plots, such as
line plots, scatter plots, bar plots, histograms, and more.
# Line plot
plt.plot(x_values, y_values)
# Scatter plot
plt.scatter(x_values, y_values)
# Bar plot
plt.bar(x_values, y_values)
# Histogram
plt.hist(data, bins=10)
# Adding legend
plt.legend(['Line 1', 'Line 2'])
4. Multiple Subplots: Matplotlib supports creating multiple subplots within a single figure.
# Create a figure with multiple subplots
plt.subplot(2, 2, 1) # 2 rows, 2 columns, subplot 1
plt.plot(x1, y1)
plt.subplot(2, 2, 2) # subplot 2
plt.scatter(x2, y2)
plt.subplot(2, 2, 3) # subplot 3
plt.bar(x3, y3)
plt.subplot(2, 2, 4) # subplot 4
plt.hist(data)
5. Saving Plots: You can save plots as image files (e.g., PNG, PDF) using Matplotlib.
plt.savefig('plot.png')
These are just a few examples of what you can do with Matplotlib for plotting in Python.
Matplotlib's extensive functionality and flexibility make it a versatile tool for creating a wide range
of visualizations.
____________________________________________________________
4. Seaborn for Statistical plots - Seaborn is a Python visualization library based on
Matplotlib that provides a high-level interface for creating attractive and informative statistical
plots. It simplifies the process of creating complex visualizations by providing easy-to-use functions
with sensible defaults. Here's an overview of how you can use Seaborn for statistical plots:
1. Import Seaborn: Begin by importing the Seaborn library.
import seaborn as sns
2. Loading Data: Seaborn works well with Pandas DataFrames, so you can load your data into
a DataFrame and pass it to Seaborn functions.
import pandas as pd
3. Creating Statistical Plots: Seaborn provides functions to create various types of statistical
plots with minimal code.
# Scatter plot with linear regression line
sns.regplot(x='x_column', y='y_column', data=df)
4. Customizing Plots: Seaborn allows you to customize the appearance of plots using various
parameters.
# Changing color palette
sns.scatterplot(x='x_column', y='y_column', data=df, palette='muted')
5. Faceting: Seaborn supports faceting, which allows you to create multiple plots based on
subsets of the data.
# Facet grid for visualizing relationships by categories
g = sns.FacetGrid(df, col='category_column')
g.map(sns.histplot, 'numeric_column')
6. Statistical Estimation: Seaborn provides functions for statistical estimation within plots.
# Kernel density estimation plot
sns.kdeplot(df['numeric_column'])
These are just a few examples of what you can do with Seaborn for creating statistical plots in
Python. Seaborn's simplicity and aesthetics make it a popular choice for data visualization,
especially for exploring and presenting relationships in your data.
____________________________________________________________
2. Bokeh: Bokeh is a Python library that provides interactive plotting capabilities for web
browsers. It allows you to create interactive plots with minimal code using high-level APIs.
Bokeh supports a variety of plot types and provides tools for adding interactivity to your
visualizations.
from bokeh.plotting import figure, show
3. Plotly Express: Plotly Express is a high-level interface for Plotly that provides a simplified
API for creating interactive plots. It offers functions for creating a wide range of plot types
with minimal code, making it easy to create interactive visualizations quickly.
import plotly.express as px
These libraries allow you to create interactive visualizations that enhance data exploration and
presentation. You can customize your plots with features like tooltips, zooming, panning, and more
to provide a rich interactive experience for your audience.
____________________________________________________________
6. SciKit for Machine learning - Scikit-learn, also known as sklearn, is a popular
Python library for machine learning. It provides simple and efficient tools for data mining and data
analysis, built on top of NumPy, SciPy, and Matplotlib. Here's an overview of some of the key
features and functionalities of scikit-learn:
1. Consistent API: Scikit-learn provides a uniform and consistent API for various machine
learning algorithms, making it easy to switch between different models and techniques.
2. Supervised Learning: Scikit-learn includes a wide range of supervised learning algorithms
for classification, regression, and clustering. These algorithms include linear models, support
vector machines, decision trees, random forests, k-nearest neighbors, and more.
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestRegressor
4. Model Evaluation: Scikit-learn provides tools for model evaluation and selection, including
metrics for classification, regression, and clustering tasks. You can use functions like
cross_val_score and GridSearchCV for hyperparameter tuning and model
selection.
from sklearn.model_selection import cross_val_score, GridSearchCV
from sklearn.metrics import accuracy_score, mean_squared_error
5. Data Preprocessing: Scikit-learn includes utilities for data preprocessing, such as scaling,
normalization, encoding categorical variables, and handling missing values.
from sklearn.preprocessing import StandardScaler, OneHotEncoder, Imputer
6. Pipeline: Scikit-learn allows you to create data processing and modeling pipelines, enabling
you to chain multiple processing steps and models together in a single workflow.
from sklearn.pipeline import Pipeline
7. Integration with NumPy and Pandas: Scikit-learn seamlessly integrates with NumPy
arrays and Pandas DataFrames, making it easy to work with data in different formats.
import numpy as np
import pandas as pd
Scikit-learn is well-documented with extensive tutorials and examples, making it accessible for both
beginners and experienced users. It's widely used in academia and industry for a variety of machine
learning tasks, from simple classification problems to complex data analysis pipelines.
____________________________________________________________