Site icon Dheeraj Patidar

Python Programming Full Course (All In One)

Python Programming

(Unit - 1, 2, 3, 4, 5)

Contents

Unit 1: Introduction to Python Programming

Introduction to Python Programming Language

Python is a widely used programming language that offers simplicity, readability, and versatility to developers across various domains. Created by Guido van Rossum and first released in 1991, Python has gained immense popularity for its clean syntax and design philosophy focused on code readability. In this introduction, we will delve into the key aspects of Python and explore why it has become a preferred language among programmers.

  1. History and Design Philosophy: Python was developed by Guido van Rossum during the late 1980s and early 1990s. It was designed as an interpreted, high-level programming language with a primary focus on simplicity and readability. Guido aimed to create a language that would emphasize code clarity and elegance, leading to the birth of Python. The guiding principles behind Python’s design philosophy are often referred to as the “Zen of Python,” promoting clean, explicit, and concise code.
  2. General Purpose and Versatility: Python is a general-purpose programming language, which means it can be used for a wide range of applications. Whether you want to build web applications, automate tasks, process data, or develop artificial intelligence models, Python provides the necessary tools and libraries to accomplish these tasks efficiently. Python supports various programming paradigms, including procedural, object-oriented, and functional programming, making it adaptable to different coding styles and requirements.
  3. Readable Syntax: Python’s syntax is known for its simplicity and readability, making it easy for developers to understand and write code. Python uses whitespace indentation, rather than explicit braces or keywords, to define code blocks. This indentation-based approach enhances code consistency and visual appeal. Additionally, Python’s syntax resembles natural language, which contributes to its ease of learning and comprehension for both beginners and experienced programmers.
  4. Extensive Standard Library and Third-Party Packages: Python comes with a rich standard library that offers numerous modules and packages for various tasks, such as file I/O, network communication, and regular expressions. The standard library eliminates the need for developers to write code from scratch for common functionalities, enabling them to leverage existing solutions. Moreover, Python has a vast ecosystem of third-party packages and frameworks, such as NumPy, Pandas, Django, and TensorFlow, which extend Python’s capabilities for specific domains and purposes.
  5. Cross-Platform Compatibility: Python is a cross-platform language, meaning it can run on different operating systems such as Windows, macOS, and various Unix-based systems. This cross-platform compatibility allows developers to write code on one platform and effortlessly execute it on another without significant modifications. Python’s ability to seamlessly work across different environments contributes to its versatility and popularity among developers.
  6. Interpreted and Interactive Nature: Python is an interpreted language, which means that it does not require compilation before execution. Developers can write code and directly execute it without the need for a separate compilation step. This feature facilitates rapid development and prototyping, making Python well-suited for scripting and interactive programming. Python also provides interactive shells, such as the Python REPL (Read-Eval-Print Loop), which allows developers to experiment with code snippets and test ideas in real-time.
  7. Supportive Community: Python boasts a large and supportive community of developers, researchers, and enthusiasts who actively contribute to its growth and development. This vibrant community has led to the creation of extensive documentation, online forums, and resources that are readily available to assist both beginners and experienced programmers. The Python Software Foundation (PSF) oversees the development of Python and provides support and guidance to the community.

Characteristrics of Python:

Python is a high-level, interpreted programming language known for its simplicity, readability, and versatility. It offers several characteristics that contribute to its popularity and widespread adoption:

These characteristics make Python a versatile language suitable for a wide range of applications, including web development, scientific computing, data analysis, machine learning, automation, scripting, and more.

Applications of Python

Python is a versatile programming language with a wide range of applications across various domains. Here are some popular applications of Python:

  1. Web Development: Python is widely used for building web applications and websites. Frameworks like Django and Flask make web development in Python efficient and scalable.
  2. Data Analysis and Visualization: Python, along with libraries such as Pandas, NumPy, and Matplotlib, is extensively used for data analysis, manipulation, and visualization. It is popular among data scientists and analysts for tasks like data cleaning, exploration, and building machine learning models.
  3. Machine Learning and Artificial Intelligence: Python has become the de facto language for machine learning and AI projects. Libraries like TensorFlow, Keras, and PyTorch provide powerful tools for building and training neural networks and other machine learning models.
  4. Scientific Computing: Python, combined with libraries like SciPy, provides a robust environment for scientific computing and simulations. It is widely used in fields like physics, chemistry, biology, and engineering for tasks such as numerical analysis, optimization, and solving differential equations.
  5. Automation and Scripting: Python’s simplicity and ease of use make it an ideal language for automating repetitive tasks and writing scripts. It is commonly used for tasks like file handling, data parsing, and system administration.
  6. Internet of Things (IoT): Python is often used for programming IoT devices and connecting them to the cloud. Its lightweight nature, combined with libraries like PySerial and MQTT, makes it suitable for IoT applications.
  7. Game Development: Python has gained popularity in the game development community. Libraries like Pygame provide a framework for creating 2D games, and engines like Panda3D and Pyglet offer more advanced capabilities for 3D game development.
  8. Desktop Applications: Python can be used for developing desktop applications with graphical user interfaces (GUIs). Frameworks like Tkinter, PyQt, and wxPython enable developers to build cross-platform applications with ease.
  9. Scripting for Software Testing: Python is commonly used for scripting and automating software testing. Its simplicity and readability make it a popular choice for writing test scripts and running test cases.
  10. Web Scraping: Python’s libraries, such as BeautifulSoup and Scrapy, are widely used for web scraping. They enable developers to extract data from websites, automate data collection, and perform tasks like web monitoring and data mining.

Basics of Python Programming

Literals

Python Literals can be defined as data that is given in a variable or constant.

In Python, literals are used to represent values of different types directly in your code. They are constant values that are not assigned to variables but are directly used to represent specific data. Python supports several types of literals. Let’s go through each of them with examples:

Numeric literals:
Boolean literals:

These represent the truth values True and False, indicating logical conditions. It’s important to note that the capitalization is significant; the first letter must be uppercase. For example: True, False.

None literal:

This represents the absence of a value or a null value. It is denoted by the keyword None. It is often used to indicate the absence of a return value or as a placeholder. For example: None.

Binary Literals:

Binary literals are prefixed with 0b or 0B followed by a sequence of binary digits (0s and 1s). Each digit represents a single binary bit.

Here’s an example of a binary literal:

binary_num = 0b1010
print(binary_num)  # Output: 10

In this example, 0b1010 represents the binary number 1010, which is equivalent to the decimal number 10. The 0b prefix indicates that the following digits are in binary format.

You can perform various operations on binary literals, such as arithmetic operations and bitwise operations. Here’s an example that demonstrates addition and bitwise AND operation with binary literals:

binary_num1 = 0b1010
binary_num2 = 0b1100

 

addition_result = binary_num1 + binary_num2
bitwise_and_result = binary_num1 & binary_num2

 

print(addition_result)   # Output: 22 (decimal)
print(bin(addition_result))   # Output: 0b10110 (binary)

print(bitwise_and_result)    # Output: 8 (decimal)

print(bin(bitwise_and_result))   # Output: 0b1000 (binary)

In this example, we add 0b1010 (10 in decimal) and 0b1100 (12 in decimal), resulting in 0b10110 (22 in decimal). The bin() function is used to convert the results back to binary representation for printing.

Binary literals are useful when working with binary data or performing bitwise operations. They provide a concise and convenient way to represent binary values directly in your code.

List literals:

These represent ordered collections of items enclosed in square brackets ([]). The items can be of any type, and they are separated by commas. For example: [1, 2, 3], [‘apple’, ‘banana’, ‘orange’], [True, 3.14, ‘Hello’].

Tuple literals:

These are similar to lists, but they are enclosed in parentheses (()). Unlike lists, tuples are immutable, meaning their elements cannot be changed after creation. For example: (1, 2, 3), (‘apple’, ‘banana’, ‘orange’), (True, 3.14, ‘Hello’).

Dictionary literals:

These represent key-value pairs enclosed in curly braces ({}). Each key-value pair is separated by a colon (:), and the pairs are separated by commas. For example: {‘name’: ‘John’, ‘age’: 25}, {‘fruit’: ‘apple’, ‘color’: ‘red’}.

Set literals:

]These represent an unordered collection of unique elements enclosed in curly braces ({}). The elements are separated by commas. For example: {1, 2, 3}, {‘apple’, ‘banana’, ‘orange’}.

Bytes literals:

These represent sequences of bytes enclosed in either single quotes (b’…’) or double quotes (b”…”). They are used to represent binary data. For example: b’Hello’, b”\x41\x42\x43″.

Bytearray literals:

These are similar to bytes literals but are mutable. They are represented as bytearray(b’…’) or bytearray(b”…”). For example: bytearray(b’Hello’), bytearray(b”\x41\x42\x43″).

These are the main types of literals in Python, representing different types of data. By using literals, you can directly include constant values in your code without the need for variables or computations.

I know you are still in confusion. When I was beginner, I was also confused in first time but when I keep learnig python concept one by one my old doubts got clear. so I think you should kep reading this course. Once you learn the data types in oython then you will get clarity about literals as well.

Variables and Identifiers

In Python, variables are used to store data values, while identifiers are names given to entities like variables, functions, classes, modules, etc. An identifier is a user-defined name used to identify a specific element in a program.

Here are some key points about variables and identifiers in Python:

Variables:
Identifiers:

It’s important to choose meaningful and descriptive names for your variables and identifiers to make your code more readable and understandable.

Operators

In Python, operators are symbols or special characters that perform various operations on operands (values or variables). Python supports a wide range of operators, which can be classified into different categories based on their functionality. Here are some of the common types of operators in Python:

1. Arithmetic Operators:
2. Assignment Operators:
3. Comparison Operators:
4. Logical Operators:
5. Bitwise Operators:
6. Membership Operators:
7. Identity Operators:

Here is a single python program to understand all operators practically:

# Arithmetic Operators
a = 10 + 5
b = 12 – 3
c = 6 * 4
d = 20 / 5
e = 15 // 4
f = 15 % 4
g = 2 ** 3

 

# Assignment Operators
x = 10
x += 5
y = 20
y -= 3
z = 6
z *= 4
w = 20
w /= 5
p = 15
p //= 4
q = 15
q %= 4
r = 2
r **= 3

 

# Comparison Operators
num1 = 10
num2 = 5
equal = num1 == num2
not_equal = num1 != num2
greater_than = num1 > num2
less_than = num1 < num2
greater_than_equal = num1 >= num2
less_than_equal = num1 <= num2

 

# Logical Operators
logical_and = True and False
logical_or = True or False
logical_not = not True

 

# Bitwise Operators
bitwise_and = 5 & 3
bitwise_or = 5 | 3
bitwise_xor = 5 ^ 3
bitwise_not = ~5
left_shift = 5 << 2
right_shift = 5 >> 1

 

# Membership Operators
sequence = [1, 2, 3, 4]
membership_in = 2 in sequence
membership_not_in = 5 not in sequence

 

# Identity Operators
obj1 = “Hello”
obj2 = “Hello”
identity_is = obj1 is obj2
identity_is_not = obj1 is not obj2

 

# Print results
print(“Arithmetic Operators:”)
print(a, b, c, d, e, f, g)
print(“\nAssignment Operators:”)
print(x, y, z, w, p, q, r)
print(“\nComparison Operators:”)
print(equal, not_equal, greater_than, less_than, greater_than_equal, less_than_equal)
print(“\nLogical Operators:”)
print(logical_and, logical_or, logical_not)
print(“\nBitwise Operators:”)
print(bitwise_and, bitwise_or, bitwise_xor, bitwise_not, left_shift, right_shift)
print(“\nMembership Operators:”)
print(membership_in, membership_not_in)
print(“\nIdentity Operators:”)
print(identity_is, identity_is_not)

Expressions

Constant Expressions:

Constant expressions are expressions that involve only literal values. These values do not change during the execution of the program. For example:

result = 10 + 5    # 10 and 5 are constant expressions

Arithmetic Expressions:

Arithmetic expressions involve mathematical operations like addition, subtraction, multiplication, division, etc. Here’s an example:

x = 10
y = 5
addition = x + y # addition is an arithmetic expression

Integral Expressions:

Integral expressions involve operations on integer values. For example:

x = 10
y = 3
division = x // y # division is an integral expression, // performs integer division

Floating Expressions:

Floating expressions involve operations on floating-point values. Here’s an example:

x = 3.5
y = 2.0
multiplication = x * y # multiplication is a floating expression

Relational Expressions:

Relational expressions compare values and return a boolean result (True or False). For example:

x = 5
y = 3
is_greater = x > y # is_greater is a relational expression

Logical Expressions:

Logical expressions involve logical operations like AND, OR, and NOT. Here’s an example:

a = True
b = False
logical_result = a and b # logical_result is a logical expression

Bitwise Expressions:

Bitwise expressions operate on the binary representation of values. They involve operations like bitwise AND, OR, XOR, etc. For example:

x = 5
y = 3
bitwise_result = x & y # bitwise_result is a bitwise expression

Combinational Expressions:

Combinational expressions involve multiple operators and operands. They can combine arithmetic, logical, and relational operations. Here’s an example:

x = 5
y = 3
result = (x + y) * 2 > 10 # result is a combinational expression

Multiple Operators in Expression (Operator Precedence):

In Python, expressions can involve multiple operators, and the order of evaluation is determined by operator precedence. For example:

x = 5
y = 3
result = x + y * 2 # Multiplication has higher precedence than addition

In the above example, `y * 2` is evaluated first, and then the result is added to `x`.

It’s important to consider operator precedence to ensure that expressions are evaluated correctly. If needed, parentheses can be used to specify the desired order of evaluation.

Operator Precedence

Operator precedence in Python determines the order in which operators are evaluated within an expression. It ensures that expressions are computed correctly by following predefined rules. Operators with higher precedence are evaluated before those with lower precedence.

Here’s a summary of the operator precedence in Python, from highest to lowest

When multiple operators of the same precedence are present, their evaluation order is determined by their associativity. Most operators in Python are left-associative, meaning they are evaluated from left to right. The exponentiation operator (`**`) is right-associative, so it is evaluated from right to left.

Consider the following example:

result = 5 + 3 * 2 ** 2 / 4

In this expression, operator precedence dictates that `2 ** 2` is evaluated first, resulting in 4. Then, `3 * 4` is evaluated, yielding 12. Next, `12 / 4` is computed, resulting in 3. Finally, `5 + 3` is evaluated, yielding 8. Therefore, the final value assigned to `result` is 8.

Understanding operator precedence is important for writing expressions that evaluate accurately. Parentheses can be used to explicitly specify the desired order of operations, overriding the default precedence. This helps in making expressions more readable and ensures that the computations are performed as intended.

Data Types in Python

Numeric Types

Integer (`int`): The size of an integer depends on the platform and Python version. In most implementations, it typically uses either 4 bytes (32 bits) or 8 bytes (64 bits). Example: age = 25.

Float (`float`): Floats are typically stored using 8 bytes (64 bits). Example: pi = 3.14.

Complex (`complex`): Complex numbers are represented using two floats, so they usually occupy 16 bytes (128 bits). Example: z = 2 + 3j.

Sequence Types

String (`str`): The size of a string depends on the number of characters it contains. Each character in a string takes 1 byte, and additional memory is required for storing the length and other metadata. Example: name = “John”.

List (`list`): The size of a list is proportional to the number of elements it contains. It requires additional memory for storing references to the elements. Example: numbers = [1, 2, 3, 4, 5].

Tuple (`tuple`): Similar to lists, the size of a tuple depends on the number of elements it contains. Example: coordinates = (10, 20).

Range (`range`): The size of a range object is determined by the start, stop, and step values provided during its creation. Example: count = range(1, 10, 2).

Mapping Type:

Dictionary (`dict`): The size of a dictionary depends on the number of key-value pairs it contains. It requires additional memory for storing hash tables and references to the keys and values. Example: person = {“name”: “John”, “age”: 30, “city”: “New York”}.

Set Types:

Set (`set`): The size of a set depends on the number of unique elements it contains. It requires additional memory for storing hash tables and references to the elements. Example: fruits = {“apple”, “banana”, “orange”}.

FrozenSet (`frozenset`): Similar to sets, the size of a frozen set depends on the number of unique elements it contains.

Boolean Type:

Boolean (`bool`): Booleans typically occupy 1 byte of memory. Example: is_valid = True.

None Type:

None (`NoneType`): The size of the `None` object itself is usually small and constant. Example: result = None.

It’s important to note that the actual memory usage may vary depending on the Python implementation, platform, and other factors. The sizes mentioned here are rough estimates and can change in different contexts.

Control Structures in python

Python provides several control structures that allow you to control the flow of execution in your programs. The most common control structures in Python are:

Conditional Statements (if-elif-else):

Conditional statements allow you to execute different blocks of code based on certain conditions. The basic syntax is:

if condition1:
     # code block executed if condition1 is True
elif condition2:
     # code block executed if condition1 is False and condition2 is True
else:
    # code block executed if both condition1 and condition2 are False

Here, `condition1`, `condition2`, etc., are expressions that evaluate to either `True` or `False`.

Example:

x = 10

if x > 0:
  print(“x is positive”)
elif x < 0:
  print(“x is negative”)
else:
  print(“x is zero”)

Output:

x is positive

Loops:

Python provides two types of loops: `for` and `while` loops.

For Loop:

The `for` loop allows you to iterate over a sequence (such as a list, tuple, string, or range) or any other iterable object. The basic syntax is:

for item in iterable:
    # code block executed for each item in the iterable

Example:

fruits = [“apple”, “banana”, “cherry”]

for fruit in fruits:
print(fruit)

Output:

apple
banana
cherry

While Loop:

The `while` loop repeatedly executes a block of code as long as a specified condition is `True`. The basic syntax is:

while condition:
     # code block executed while the condition is True

It’s important to ensure that the condition eventually becomes `False`; otherwise, the loop will run indefinitely.

Example:

count = 0

while count < 5:
print(count)
count += 1

Output:

0
1
2
3
4

Break and Continue:

Within loops, you can use the `break` statement to exit the loop prematurely, and the `continue` statement to skip the rest of the current iteration and move to the next iteration.

for item in iterable:
     if condition:
        break # exit the loop
     if condition:
        continue # skip the rest of the code block and move to the next iteration

Example:

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

for num in numbers:
if num == 3:
continue # skip the number 3
if num == 8:
break # exit the loop when reaching 8
print(num)

Output:

1
2
4
5
6
7

Exception Handling (try-except-else-finally):

Exception handling allows you to handle and recover from runtime errors gracefully. The basic syntax is:

try:
    # code block where an exception may occur
except ExceptionType1:
    # code block executed if ExceptionType1 is raised
except ExceptionType2:
    # code block executed if ExceptionType2 is raised
else:
    # code block executed if no exceptions are raised
finally:
    # code block executed regardless of whether an exception is raised or not

You can have multiple `except` blocks to handle different types of exceptions.

Example:

try:
     x = 10 / 0 # divide by zero to trigger an exception
except ZeroDivisionError:
     print(“Error: Division by zero”)
else:
     print(“Result:”, x)
finally:
    print(“End of program”)

Output:

Error: Division by zero
End of program

Lists & Tuples

List Structures:

In Python, a list is a built-in data structure that allows you to store and manipulate a collection of items. Lists are mutable, meaning you can modify their elements after they are created. Lists can contain elements of different data types, such as integers, strings, or even other lists. Lists are defined using square brackets `[ ]`, and the elements are separated by commas.

Here’s an example of a list that contains integers:

numbers = [1, 2, 3, 4, 5]

And here’s an example of a list that contains strings:

fruits = [“apple”, “banana”, “orange”]

Lists in Python can also be empty, meaning they contain no elements:

empty_list = []

Lists can be accessed and modified using their index values. The first element of the list has an index of 0, the second element has an index of 1, and so on. Negative indices can be used to access elements from the end of the list. For example, `-1` refers to the last element, `-2` refers to the second-to-last element, and so on.

Lists in Python are dynamic, which means you can add, remove, or modify elements. Some commonly used list operations include:

Accessing elements: list[index]

Modifying elements: list[index] = new_value

Appending elements: list.append(element)

Removing elements: list.remove(element)

Checking the length: len(list)

Iterating over Lists in Python:

To iterate over the elements of a list in Python, you can use various methods, such as a `for` loop or a `while` loop. The most common approach is to use a `for` loop.

Here’s an example that demonstrates how to iterate over a list using a `for` loop:

fruits = [“apple”, “banana”, “orange”]

for fruit in fruits:
    print(fruit)

Output:

apple
banana
orange

In each iteration of the loop, the variable `fruit` takes the value of the current element in the list, allowing you to perform operations or access the element as needed.

You can also use the `range()` function in combination with the length of the list (`len(list)`) to iterate over the indices of a list:

fruits = [“apple”, “banana”, “orange”] 

for i in range(len(fruits)):
    print(fruits[i])

Output:

apple
banana
orange

In this example, `i` takes on the values `0`, `1`, and `2`, which are used as indices to access the corresponding elements in the list.

Iterating over a list allows you to perform operations on each element or use the elements in other parts of your program. It’s a powerful way to process and manipulate data stored in lists.

Modular Design and Modules:

Modular design is an approach to organizing code into separate, self-contained modules that perform specific tasks. Each module focuses on a specific functionality and can be developed and tested independently. Modules provide a way to break down complex problems into smaller, more manageable parts.

In Python, a module is a file that contains Python definitions and statements. It serves as a container for related functions, classes, and variables. Modules allow code reuse, maintainability, and logical separation of concerns.

Example:

Let’s create a module named “math_operations.py” that defines some basic mathematical operations.

math_operations.py:

def add(a, b):
return a + b

 

def subtract(a, b):
return a – b

 

def multiply(a, b):
return a * b

 

def divide(a, b):
return a / b

We can then import and use this module in another Python script:

main.py:

import math_operations

result = math_operations.add(5, 3)
print(result) # Output: 8

result = math_operations.multiply(4, 2)
print(result) # Output: 8

Top-Down Design:

Top-down design is a problem-solving approach that involves breaking down a complex problem into smaller, more manageable sub-problems. It starts with the high-level overview of the problem and gradually decomposes it into smaller tasks until each task becomes simple enough to be implemented.

Example:

Suppose we want to create a program to calculate the factorial of a number using top-down design.

Identify the high-level problem: Calculate the factorial of a number.

Break down the problem into smaller tasks:

Implement each sub-task and test it individually.

By following this top-down approach, we can tackle complex problems by breaking them down into smaller, more manageable parts, making the overall development process more organized and easier to manage.


Unit 2: Functions & File Handling

Functions

In Python, a function is a block of code that performs a specific task. It can take arguments as input, perform operations on them, and optionally return a value.

Example:

def greet(name):
     print(“Hello, ” + name + “!”)

 

greet(“Alice”)

Output:

Hello, Alice!

In the above example, the `greet` function takes an argument `name` and prints a greeting message using that argument.

Types of functions in python

In Python, there are different types of functions based on their functionality and usage. Here are the most commonly used types of functions in Python:

1. Built-in functions:

These are pre-defined functions that come with Python. They are designed to perform common tasks like printing output, converting data types, sorting and searching lists, and more. Examples include `print()`, `str()`, `int()`, `sum()`, `len()`, and more.

2. User-defined functions:

These are functions that are created by the programmer to perform a specific task or set of tasks. They allow for code reuse, and make the code more modular and easier to read. User-defined functions are defined using the `def` keyword in Python and can take any number of arguments and return any value.

Example:

def add_numbers(x, y):

   return x + y

   result = add_numbers(4, 5)

print(result)    # Output: 9

3. Lambda functions:

Lambda functions, also known as anonymous functions, are small, one-line functions that do not have a name. They are useful when you need to define a simple function without writing a lot of code. Lambda functions can take any number of arguments and return a value.

Example:

add_numbers = lambda x, y: x + y

result = add_numbers(4, 5)
print(result)     # Output: 9

4. Recursion functions:

Recursion is a programming technique where a function calls itself to solve a problem. Recursion functions are useful when you need to solve a problem that can be broken down into smaller subproblems. In Python, recursion functions can be used to solve problems like calculating the factorial of a number, finding the Fibonacci sequence, and more.

Example:

def factorial(n):
if n == 0:
    return 1
else:
   return n * factorial(n-1)

   result = factorial(5)

print(result)   # Output: 120

5. Decorator functions:

Decorator functions are functions that take another function as an argument and add some functionality to it without changing its core behavior. Decorator functions are used to modify or extend the behavior of a function.

Example:

def my_decorator(func):
  def wrapper():
    print(“Before function call”)
    func()
    print(“After function call”)
  return wrapper

 

@my_decorator
def say_hello():
  print(“Hello World!”)

say_hello()    # Output: Before function call \n Hello World! \n After function call

In the above example, `my_decorator` is a decorator function that takes another function `say_hello` as an argument and adds some functionality to it. The `wrapper` function is then returned by `my_decorator`, and `say_hello` is decorated using the `@my_decorator` syntax, which adds the functionality of `my_decorator` to `say_hello`.

Types of Argumentsin in python functions:

In Python, there are different types of function arguments that can be used when defining a function:

1, Positional arguments:

Positional arguments are the most common type of function arguments in Python. They are passed to the function in the order they are defined and matched to the arguments in the function by their position.

Example:

def add_numbers(x, y):
    return x + y

 

result = add_numbers(4, 5)
print(result)    # Output: 9

In the above example, `x` and `y` are positional arguments, and they are passed to the `add_numbers` function in the order they are defined.

2. Keyword arguments:

Keyword arguments are defined with a keyword that corresponds to a parameter name. This allows arguments to be passed to the function in any order, and also lets the function be more self-documenting.

Example:

def add_numbers(x, y):
   return x + y

 

result = add_numbers(y=5, x=4)
print(result)    # Output: 9

In the above example, `x` and `y` are keyword arguments, and they are passed to the `add_numbers` function using the parameter names.

3. Default arguments:

Default arguments are used to set a default value for a parameter if no value is provided for it when the function is called. This is useful when you want to give users the option of overriding the default value.

Example:

def greet(name, greeting=”Hello”):
    print(greeting + “, ” + name + “!”)

 

greet(“Alice”) # Output: Hello, Alice!
greet(“Bob”, “Hi”) # Output: Hi, Bob!

In the above example, the `greeting` parameter has a default value of “Hello”. If an argument is not provided for it, the function will use the default value.

4. Variable-length arguments:

Variable-length arguments allow a function to accept an arbitrary number of arguments. There are two types of variable-length arguments: *args and **kwargs.

Example:

def sum_numbers(*numbers):
   total = 0
   for number in numbers:
     total += number

   return total

 

result = sum_numbers(1, 2, 3, 4)
print(result) # Output: 10

Example:

def print_items(**items):
   for key, value in items.items():
   print(key + “:”, value)

 

print_items(item1=”apple”, item2=”banana”, item3=”orange”)

In the above example, `**items` is used to accept any number of keyword arguments. The function prints the key-value pairs of all the arguments passed to it.

5. Positional-only arguments:

Positional-only arguments are new to Python 3.8. They allow a function to accept positional arguments only, without the use of keywords.

Example:

def greet(name, /, greeting=”Hello”):
   print(greeting + “, ” + name + “!”)

 

greet(“Alice”) # Output: Hello, Alice!
greet(“Bob”, “Hi”) # Error: greet() takes 1 positional argument but 2 were given

In the above example, the `/` in the function definition marks the end of the positional arguments. This means that the second argument can only be passed positionally and not by keyword.

Program Routes

In Python, a program route refers to the specific path or sequence of code execution within a program. It determines the flow of the program and the order in which instructions are executed. By defining different routes or paths, you can control the program’s behavior based on certain conditions or user inputs. There are several ways to implement program routes in Python, such as using conditional statements (if-else), loops, and function calls.

Let’s explore these concepts with examples:

1. Conditional Statements (if-else):

Conditional statements allow you to execute different blocks of code based on specific conditions. They are typically used to make decisions within a program. Here’s an example:

age = 25

 

if age >= 18:
  print(“You are an adult.”)
else:
  print(“You are a minor.”)

In this example, the program checks if the `age` variable is greater than or equal to 18. If it is, the code block under the `if` statement is executed, printing “You are an adult.” Otherwise, the code block under the `else` statement is executed, printing “You are a minor.”

2. Loops:

Loops allow you to repeat a block of code multiple times. They are useful for executing the same code with different inputs or iterating over a collection of items. Here’s an example using a `for` loop:

fruits = [“apple”, “banana”, “cherry”]

 

for fruit in fruits:
   print(“I like”, fruit)

In this example, the program iterates over each element in the `fruits` list. For each iteration, the code block under the `for` statement is executed, printing “I like [fruit]” where `[fruit]` is replaced with the actual fruit value.

3. Function Calls:

Functions are reusable blocks of code that can be called multiple times within a program. They allow you to organize your code and perform specific tasks. You can define functions and call them whenever needed. Here’s an example:

def greet(name):
   print(“Hello,”, name)

 

def farewell(name):
   print(“Goodbye,”, name)

 

greet(“Alice”)
farewell(“Bob”)

In this example, we define two functions: `greet()` and `farewell()`. Each function takes a `name` parameter and prints a corresponding greeting or farewell message. We call these functions with different arguments, resulting in different outputs.

By combining these concepts and using appropriate control structures, you can create complex program routes to handle various scenarios. It’s essential to understand the logic and order of execution in your code to ensure the desired behavior.

Calling Value-Returning Functions

Value-returning functions return a value that can be stored in a variable or used directly in expressions. Example:

def square(number):
   return number ** 2

 

result = square(5)
print(result)    # Output: 25

In the above example, the `square` function takes an argument `number` and returns its square. The returned value is stored in the variable `result` and later printed.

4. Calling Non-value Returning Functions:

Non-value returning functions do not return any value explicitly. They perform some actions or operations, but do not produce any output. Example:

def greet(name):
  print(“Hello, ” + name + “!”)

 

greet(“Bob”)

Output:

Hello, Bob!

In the above code, the `greet` function takes an argument `name` and prints a greeting message without returning any value.

Parameter Passing

Parameter passing is the process of passing arguments to a function. In Python, parameters can be passed in two ways: by value and by reference.

Passing by value means that a copy of the argument’s value is passed to the function. The function works on this copy, and any changes made to the parameter inside the function do not affect the original argument outside the function.

Passing by reference means that the actual argument is passed to the function. Any changes made to the parameter inside the function affect the original argument.

It’s important to note that in Python, all arguments are passed by reference, but the behavior may appear as passing by value in some cases.

Example:

def double(number):
    number *= 2
    print(“Inside the function: “, number)

 

x = 4
double(x)
print(“Outside the function: “, x)

Output:

Inside the function: 8
Outside the function: 4

In the above code, even though the `number` parameter is modified inside the `double` function, it does not affect the original value of `x` outside the function.

Variable Scope:

Variable scope refers to the portion of a program where a variable is accessible. In Python, there are two main types of variable scope: global scope and local scope.

Global Scope:

Variables declared outside of any function or class have global scope. They can be accessed from anywhere within the program, including inside functions and classes.

Example:

global_var = 10 # Global variable

def my_function():
   print(global_var) # Accessing global variable inside a function

 

my_function() # Output: 10

print(global_var) # Output: 10

Local Scope:

Variables declared inside a function or class have local scope. They are only accessible within the function or class where they are defined.

Example:

def my_function():
   local_var = 20   # Local variable
   print(local_var)    # Accessing local variable inside the function

 

my_function()    # Output: 20
print(local_var)   # NameError: name ‘local_var’ is not defined

File Handling

In Python, file handling refers to performing operations on files, such as reading, writing, and appending data. These operations are crucial for working with text files and allow you to manipulate their contents. Let’s explore each of these topics in detail with examples.

Reading from Text Files:

Reading from a text file involves accessing the data stored within the file and bringing it into your Python program. Here’s an example of how to read from a text file:

# Open the file in read mode
file = open(‘example.txt’, ‘r’)

 

# Read the entire contents of the file
content = file.read()

 

# Print the contents
print(content)

 

# Close the file
file.close()

In this example, we open a file named example.txt in read mode using the open() function. Then, we use the read() method to read the entire content of the file and store it in the content variable. Finally, we print the content and close the file using the close() method.

Writing to Text Files:

Writing to a text file involves creating a new file or overwriting the existing file with new content. Here’s an example of how to write to a text file:

# Open the file in write mode
file = open(‘example.txt’, ‘w’)

 

# Write content to the file
file.write(‘Hello, world!\n’)
file.write(‘This is a sample text file.’)

 

# Close the file
file.close()

In this example, we open the file named example.txt in write mode using the open() function. Then, we use the write() method to write the desired content to the file. The \n character represents a new line. Finally, we close the file using the close() method.

If the file does not exist, the open() function with mode w will create a new file. However, if the file already exists, it will be overwritten.

Appending to Text Files:

Appending to a text file involves adding new content at the end of an existing file without overwriting the existing content. Here’s an example of how to append to a text file:

# Open the file in append mode
file = open(‘example.txt’, ‘a’)

 

# Append content to the file
file.write(‘\nThis is additional content.’)

 

# Close the file
file.close()

In this example, we open the file named example.txt in append mode using the open() function with the mode a. Then, we use the write() method to append the desired content to the file. The \n character represents a new line. Finally, we close the file using the close() method.

Appending to a file is useful when you want to add new data to the existing content without deleting or modifying the previous data.

Remember to always close the file after performing the necessary operations to free up system resources and ensure data integrity.

It’s also worth noting that using the with statement in Python is a recommended approach for file handling, as it takes care of closing the file automatically. Here’s an example of reading from a file using the with statement:

# Open the file in read mode using the with statement
with open(‘example.txt’, ‘r’) as file:
   content = file.read()
   print(content)

This way, you don’t need to explicitly call the close() method, as the with statement handles it for you.


Unit 3: String Processing, Dictionary, Sets, and Exception Handling

String Processing

In this unit I’ll provide more detailed explanations along with separate programs and outputs for each concept.

String Creation:

Strings can be created using single quotes (”), double quotes (“”), or triple quotes (”’ ”’) for multiline strings. Here’s an example program:

str1 = ‘Hello, World!’      # Single quotes
str2 = “I love Python”    # Double quotes
str3 = ”’This is a
multiline string”’       # Triple quotes

print(str1)
print(str2)
print(str3)

Output:

Hello, World!
I love Python
This is a
multiline string

String Concatenation:

String concatenation is the process of combining multiple strings into a single string using the `+` operator or by simply placing the strings next to each other. Here’s an example program:

str1 = “Hello”
str2 = “World”
result = str1 + “, ” + str2 # Concatenating strings
print(result)

Output:

Hello, World

String Indexing and Slicing:

Python allows you to access individual characters within a string using indexing. The indexing starts at 0 for the first character and goes up to length – 1. Additionally, you can extract a substring (a portion of a string) using slicing. Here’s an example program:

my_string = “Python”
print(my_string[0])  # Accessing first character
print(my_string[2:5])  # Slicing from index 2 to 4
print(my_string[:3])  # Slicing from start to index 2
print(my_string[3:])  # Slicing from index 3 to end

Output:

P
tho
Pyt
hon

String Length:

The `len()` function can be used to determine the length of a string. It returns the number of characters in the string. Here’s an example program:

my_string = “Hello, World!”
print(len(my_string))   # Length of the string

Output:

13

String Methods:

Python provides numerous built-in string methods that can be used for various string operations. Here’s an example program demonstrating some commonly used methods

my_string = ” Hello, World! “
print(my_string.lower())     # Convert to lowercase
print(my_string.strip())    # Remove leading and trailing whitespace
print(my_string.replace(“Hello”, “Hi”))    # Replace occurrences of a substring
print(my_string.split(“,”))    # Split the string into a list of substrings

Output:

hello, world!
Hello, World!
Hi, World!
[‘ Hello’, ‘ World! ‘]

String Formatting:

Python provides several ways to format strings. One common method is using the `%` operator, known as the string formatting operator. Another approach is to use the `format()` method or f-strings (formatted string literals). Here’s an example program for each method:

name = “Alice”
age = 25
print(“My name is %s and I am %d years old.” % (name, age))
print(“My name is {} and I am {} years old.”.format(name, age))
print(f”My name is {name} and I am {age} years old.”)

Output:

My name is Alice and I am 25 years old.
My name is Alice and I am 25 years old.
My name is Alice and I am 25 years old.

I hope these detailed explanations and separate programs with outputs help you understand string processing in Python more effectively.

Dictionaries in Python

Dictionaries in Python are unordered collections of key-value pairs. They are also known as associative arrays or hash maps in other programming languages. Dictionaries are enclosed in curly braces `{}` and consist of key-value pairs separated by colons `:`. Here’s an example of a dictionary:

student = {
“name”: “John”,
“age”: 20,
“grade”: “A”
}

In the example above, student is a dictionary that stores information about a student. The keys are “name”, “age”, and “grade“, and their corresponding values are “John“, 20, and “A“.

You can access the values in a dictionary by using the keys. For example:

print(student[“name”]) # Output: John
print(student[“age”]) # Output: 20

To add a new key-value pair to a dictionary, you can simply assign a value to a new key:

student[“gender”] = “Male”
print(student) # Output: {‘name’: ‘John’, ‘age’: 20, ‘grade’: ‘A’, ‘gender’: ‘Male’}

To update the value of an existing key, you can assign a new value to that key:

student[“age”] = 21
print(student) # Output: {‘name’: ‘John’, ‘age’: 21, ‘grade’: ‘A’, ‘gender’: ‘Male’}

If you want to remove a key-value pair from a dictionary, you can use the `del` statement:

del student[“grade”]
print(student)    # Output: {‘name’: ‘John’, ‘age’: 21, ‘gender’: ‘Male’}

Now let’s move on to sets in Python.

Sets in Python

Sets in Python are unordered collections of unique elements. They are enclosed in curly braces `{}` or can be created using the `set()` function. Sets can only contain immutable elements such as numbers, strings, or tuples. Here’s an example of a set:

fruits = {“apple”, “banana”, “orange”}

In the example above, `fruits` is a set that contains three elements: `”apple”`, `”banana”`, and `”orange”`. Notice that duplicate elements are automatically removed in a set.

Sets support various operations for common set operations like union, intersection, difference, and more. Let’s explore these operations using examples:

1. Union (`|`): Combines two sets and returns a new set containing all unique elements from both sets.

set1 = {1, 2, 3}
set2 = {3, 4, 5}
union_set = set1 | set2
print(union_set) # Output: {1, 2, 3, 4, 5}

2. Intersection (`&`): Returns a new set containing common elements between two sets.

set1 = {1, 2, 3}
set2 = {3, 4, 5}
intersection_set = set1 & set2
print(intersection_set) # Output: {3}

3. Difference (`-`): Returns a new set containing elements present in the first set but not in the second set.

set1 = {1, 2, 3}
set2 = {3, 4, 5}
difference_set = set1 – set2
print(difference_set)   # Output: {1, 2}

4. Symmetric Difference (`^`): Returns a new set containing elements present in either set, but not both.

set1 = {1, 2, 3}
set2 = {3, 4, 5}
symmetric_difference_set = set1 ^ set2
print(symmetric_difference_set) # Output: {1, 2, 4, 5}

These are just a few examples of the operations you can perform with dictionaries and sets in Python. They provide powerful tools for managing and manipulating data in your programs.

This is all in one cource that’s why I can’t use too much example in order to maintain user inrface of the course. I will try to share my all personal programs & projects with you in upcoming posts so turn on the notification of this site.

Exception handling

Exception handling is a fundamental concept in programming that enables the management of errors or exceptional situations that may arise during program execution.

In Python, exceptions are objects representing errors, and they can be captured and processed using try-except blocks.

A try-except block consists of a try block where you write the code that might raise an exception and an except block where you handle the exception.

When an exception occurs within the try block, the execution is transferred to the corresponding except block, and the exception is caught and handled. This approach allows your program to gracefully deal with errors without crashing.

Let’s illustrate exception handling in Python with an example:

try:
    # Code that may raise an exception
    num1 = int(input(“Enter a number: “))
    num2 = int(input(“Enter another number: “))
    result = num1 / num2
    print(“Result:”, result)
except ZeroDivisionError:
   print(“Error: Cannot divide by zero.”)
except ValueError:
   print(“Error: Invalid input. Please enter a number.”)

In this example, the try block attempts to perform a division operation by accepting two numbers from the user. If the user enters a non-numeric value or tries to divide by zero, exceptions will be raised.

The except block for ZeroDivisionError catches the specific exception that occurs when attempting to divide by zero. It handles this exception by printing an error message.

The except block for ValueError catches the specific exception that occurs when the input is not a valid number. It handles this exception by printing an error message.

You can also capture multiple exceptions in a single except block or use a generic except block to catch any exception:

try:
    # Code that may raise an exception
except (Exception1, Exception2):
    # Handle specific exceptions
except:
    # Handle any other exception

Exceptions Data Collections applying lists

Now let’s discuss the use of data collections in exception handling. In Python, you can utilize data collections such as lists to store multiple exceptions or error messages.

This approach proves beneficial when you want to keep track of multiple errors that occurred during program execution.

Consider the following example that demonstrates the utilization of a list to store exceptions:

errors = []

try:
     # Code that may raise an exception
     num1 = int(input(“Enter a number: “))
     num2 = int(input(“Enter another number: “))
     result = num1 / num2
     print(“Result:”, result)
except ZeroDivisionError:
    errors.append(“Error: Cannot divide by zero.”)
except ValueError:
    errors.append(“Error: Invalid input. Please enter a number.”)

 

# Check if any errors occurred
if errors:
   print(“The following errors occurred:”)
   for error in errors:
     print(error)

In this example, instead of directly printing the error messages, we append them to the errors list using the append() method. After the try-except block, we check if any errors were added to the list. If so, we iterate over the list and print each error message.

By utilizing a data collection like a list, you can gather and process multiple exceptions or errors, providing more comprehensive information to the user or enabling further analysis within your program.

Remember, exception handling is a powerful mechanism for error management, improving code stability, and enhancing the user experience by gracefully handling unexpected situations.


Unit 4: Object Oriented Programming

Introduction to Object-Oriented Programming in Python

Object-Oriented Programming (OOP) is a programming paradigm that revolves around the concept of objects. Python, being an object-oriented programming language, provides robust support for creating and working with objects. In this introduction, we will cover the fundamental concepts of OOP in Python.

1. Objects and Classes:

2. Attributes and Methods:

3. Encapsulation:

4. Inheritance:

5. Polymorphism:

6. Abstraction:

7. Object Lifecycle:

8. Class Variables and Instance Variables:

These are the key concepts in understanding object-oriented programming in Python. By utilizing these principles, you can build modular, reusable, and scalable code. As you delve deeper into OOP, you’ll discover more advanced topics like composition, interfaces, and design patterns, which further enhance your ability to create robust applications.

If you are begineer in programming world then I know you didn’t get anaything which I have explained.

Don’t worry It’s normal for all programmers when then Encounter with new programming topic.

Let me explain each perspective of OPP in detail with appropriate examples.

Objects and Classes:

Objects and classes are fundamental concepts in object-oriented programming. Let’s explore each of them in detail:

Classes:

Example:

class Car:
    # Class attributes
    manufacturer = “Ford”
    year = 2021

 

   # Class method
   def start_engine(self):
        print(“Engine started.”)

 

# Creating objects (instances) of the Car class
car1 = Car()
car2 = Car()

 

# Accessing class attributes
print(car1.manufacturer)   # Output: Fordprint(car2.year) # Output: 2021

 

# Calling a class method
car1.start_engine()    # Output: Engine started.

Objects:

Example:

# Creating a class
class Person:
      # Class attributes
      species = “Homo sapiens”

 

     # Constructor (initializes instance variables)
     def __init__(self, name, age):
          self.name = name
          self.age = age

 

     # Instance method
     def greet(self):
          print(f”Hello, my name is {self.name}.”)

 

# Creating objects (instances) of the Person class
person1 = Person(“John”, 25)
person2 = Person(“Alice”, 30)

# Accessing object attributes
print(person1.name)      # Output: John
print(person2.age)     # Output: 30

# Calling object methods
person1.greet()     # Output: Hello, my name is John.
person2.greet()     # Output: Hello, my name is Alice.

In the above examples, the Car class serves as a blueprint for creating car objects. Each car object (car1 and car2) has its own unique attributes, such as manufacturer and year, defined in the class. The start_engine() method represents a behavior associated with each car object and can be called individually.

Similarly, the Person class acts as a blueprint for creating person objects. Each person object (person1 and person2) has its own distinct attributes, such as name and age, along with the shared species attribute defined in the class. The greet() method represents a behavior associated with each person object.

Classes allow you to create multiple instances (objects) with their own specific state while providing a common structure and behavior defined by the class. This promotes code reusability, modularity, and organization in object-oriented programming.

Attributes and Methods in Python:

Attributes and methods are fundamental components of objects and classes in Python. Let’s explore each of them in detail:

Attributes:

Attributes can be classified as either instance attributes or class attributes.

Instance Attributes:
Class Attributes:

Example:

class Circle:
    # Class attribute
    pi = 3.14159

   def __init__(self, radius):
       # Instance attribute
       self.radius = radius

 

# Creating objects (instances) of the Circle class
circle1 = Circle(5)
circle2 = Circle(3)

 

# Accessing instance attributes
print(circle1.radius)      # Output: 5
print(circle2.radius)     # Output: 3

 

# Accessing class attribute
print(Circle.pi)       # Output: 3.14159

Methods:

Instance Methods:
Class Methods:

Example:

class Rectangle:
     def __init__(self, length, width):
          self.length = length
          self.width = width

 

    # Instance method
    def calculate_area(self):
         return self.length * self.width

 

   @classmethod
   def create_square(cls, side):
         return cls(side, side)

 

# Creating objects (instances) of the Rectangle class
rectangle = Rectangle(4, 6)

 

# Calling instance method
area = rectangle.calculate_area()
print(area)    # Output: 24

 

# Calling class method
square = Rectangle.create_square(5)
print(square.length)     # Output: 5
print(square.width)     # Output: 5

In the above example, the Circle class has both a class attribute (pi) and an instance attribute (radius). Each circle object has its own radius attribute, while the pi attribute remains the same for all circles.

Similarly, the Rectangle class demonstrates instance methods and a class method. The calculate_area() method is an instance method that calculates the area based on the length and width instance attributes of each rectangle object. The create_square() method is a class method that creates a square by initializing a rectangle object with equal length and width.

Attributes and methods play a crucial role in defining the data and behavior of objects in Python. They allow objects to store and manipulate data, as well as perform actions and operations based on the defined behaviors.

Encapsulation in Python:

Encapsulation is a fundamental principle of object-oriented programming that focuses on bundling data (attributes) and methods (functions) together within a class. It promotes data hiding and abstraction, allowing for better organization and control over the internal workings of a class. In Python, encapsulation is achieved through access specifiers and property decorators.

Access Specifiers:

Access specifiers determine the visibility and accessibility of class members (attributes and methods).

Python provides three access specifiers: public, private, and protected.

Public Access (default):
Private Access:
Protected Access:

Example:

class BankAccount:
     def __init__(self):
          self._balance = 0 # Protected attribute
          self.__account_number = 123      # Private attribute

 

    def deposit(self, amount):
          self._balance += amount

 

    def __withdraw(self, amount):        # Private method
         self._balance -= amount

 

   def get_account_info(self):
        print(f”Account Number: {self.__account_number}”)     # Accessing private attribute
        print(f”Balance: {self._balance}”)      # Accessing protected attribute

 

account = BankAccount()
account.deposit(1000)
account.get_account_info()

In the above example, the BankAccount class has a protected attribute _balance and a private attribute __account_number. The deposit() method is a public method that can modify the protected attribute _balance, while the __withdraw() method is a private method that can only be accessed within the class. The get_account_info() method is a public method that can access both the private and protected attributes.

Property Decorators:

Example:

class Person:
     def __init__(self, name):
          self._name = name

 

    @property
    def name(self):
         return self._name

 

   @name.setter
   def name(self, value):
        if len(value) > 0:
           self._name = value

 

person = Person(“John”)
print(person.name)     # Output: John

 

person.name = “Alice”
print(person.name)    # Output: Alice

 

person.name = “”       # Ignored as name cannot be empty
print(person.name)      # Output: Alice

In the above example, the Person class encapsulates the _name attribute using property decorators. The name attribute is accessed using the @property decorator, and its value is retrieved through the getter method. The name attribute can be modified using the @name.setter decorator in the setter method, with a condition to prevent an empty name.

Encapsulation helps in organizing and structuring code, hiding internal implementation details, and providing controlled access to class members. It enhances code readability, reusability, and maintainability by preventing direct access to sensitive data and enforcing proper data manipulation through defined methods.

Inheritance in Python

Inheritance is a core concept in object-oriented programming (OOP) that enables classes to inherit attributes and methods from other classes. It establishes a hierarchical relationship between classes, where a subclass can inherit properties from a superclass. In Python, inheritance is implemented using the class keyword, allowing for the creation of superclasses and subclasses.

Superclass and Subclass:

Example:

class Animal:
     def __init__(self, name):
          self.name = name

    def eat(self):
         print(f”{self.name} is eating.”)

 

class Dog(Animal):
    def __init__(self, name, breed):
         super().__init__(name)
         self.breed = breed

   def bark(self):
        print(“Woof!”)

 

# Creating instances of the Dog class
dog = Dog(“Buddy”, “Labrador”)

 

# Accessing attributes and methods
print(dog.name)    # Output: Buddy
dog.eat()     # Output: Buddy is eating.
dog.bark()    # Output: Woof!

In the example above, the Animal class serves as the superclass, while the Dog class is the subclass. The Dog class inherits the name attribute and the eat() method from the Animal class using the super() function. Additionally, the Dog class defines its own attribute breed and method bark().

Method Overriding:

Example:

class Animal:
     def make_sound(self):
     print(“Animal is making a sound.”)

 

class Cat(Animal):
     def make_sound(self):
     print(“Meow!”)

 

# Creating instances of the classes
animal = Animal()
cat = Cat()

 

# Method overriding
animal.make_sound()      # Output: Animal is making a sound.
cat.make_sound()     # Output: Meow!

In the above example, both the Animal class and the Cat class have a make_sound() method. When called on an object of the Animal class, the method from the superclass is executed. However, when called on an object of the Cat class, the method is overridden and the implementation from the subclass is invoked.

Multiple Inheritance:

Example:

class Animal:
     def eat(self):
     print(“Animal is eating.”)

 

class Flyable:
    def fly(self):
    print(“Flying…”)

 

class Bird(Animal, Flyable):
     pass

 

# Creating an instance of the Bird class
bird = Bird()

 

# Accessing attributes and methods
bird.eat() # Output: Animal is eating.
bird.fly() # Output: Flying…

In the above example, the Bird class inherits from both the Animal class and the Flyable class. As a result, the Bird class inherits the eat() method from Animal and the fly() method from Flyable.

Inheritance facilitates code reuse, promotes modularity and extensibility, and allows for the creation of specialized classes based on more general ones. It establishes an “is-a” relationship between classes, where subclasses are more specific versions of their superclasses.

Polymorphism in Python:

Polymorphism is a fundamental concept in object-oriented programming (OOP) that allows objects of different classes to be treated as objects of a common superclass. It enables methods with the same name to be invoked on different objects, resulting in different behaviors based on the object type. In Python, polymorphism is achieved through method overriding and duck typing.

Method Overriding:

Method overriding is the ability of a subclass to provide a different implementation for a method already defined in its superclass.
When a method is called on an object, the implementation of that method in the subclass is executed, overriding the implementation in the superclass.

Example:

class Animal:
    def make_sound(self):
    print(“Animal is making a sound.”)

 

class Cat(Animal):
    def make_sound(self):
    print(“Meow!”) 

 

class Dog(Animal):
    def make_sound(self):
    print(“Woof!”)

 

# Creating instances of different classes
animal = Animal()
cat = Cat()
dog = Dog()

 

# Calling the same method on different objects
animal.make_sound()     # Output: Animal is making a sound.
cat.make_sound()     # Output: Meow!
dog.make_sound()     # Output: Woof!

In the above example, the Animal class is the superclass, while the Cat and Dog classes are subclasses. Each subclass overrides the make_sound() method inherited from the superclass and provides its own implementation. When the method is called on different objects, polymorphism allows the appropriate implementation to be executed based on the object’s actual class.

Duck Typing:

Example:

class Duck:
    def quack(self):
    print(“Quack!”)

 

class Person:
    def quack(self):
         print(“I’m quacking like a duck!”)

 

def make_quack(obj):
     obj.quack()

 

# Creating instances of different classes
duck = Duck()
person = Person()

 

# Passing different objects to the same function
make_quack(duck)      # Output: Quack!
make_quack(person)     # Output: I’m quacking like a duck!

In the above example, both the Duck class and the Person class have a quack() method. The make_quack() function accepts any object and calls the quack() method on it. Even though the objects are of different classes, they can be treated interchangeably as long as they have the same method name, demonstrating polymorphic behavior through duck typing.

Polymorphism in Python allows for more flexible and modular code by enabling objects of different classes to be used interchangeably. It promotes code reusability, extensibility, and scalability, as objects can be manipulated and processed based on their common characteristics and behaviors.

Abstraction in Python

Abstraction is a fundamental concept in object-oriented programming (OOP) that involves focusing on essential features while hiding unnecessary details. It provides a simplified and generalized view of complex systems, allowing users to interact with objects at a higher level without needing to understand the internal implementation. In Python, abstraction is achieved through abstract classes, interfaces, and encapsulation.

Abstract Classes:

Example:

from abc import ABC, abstractmethod

class Animal(ABC):
     @abstractmethod
     def make_sound(self):
          pass

 

class Cat(Animal):
    def make_sound(self):
    print(“Meow!”)

 

# Creating instances of different classes
# animal = Animal() # Raises an error (cannot instantiate abstract class)
cat = Cat()

 

# Calling the method on the derived class
cat.make_sound() # Output: Meow!

In the above example, the Animal class is an abstract class defined using the ABC module and the abstractmethod decorator. It declares the make_sound() method as an abstract method without providing an implementation. The Cat class inherits from the Animal class and provides its own implementation of the make_sound() method.

Interfaces (Abstract Base Classes):

Example:

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
         pass

 

   @abstractmethod
    def perimeter(self):
        pass

 

class Rectangle(Shape):
     def __init__(self, length, width):
          self.length = length
          self.width = width

 

    def area(self):
         return self.length * self.width

    def perimeter(self):

        return 2 * (self.length + self.width)

 

# Creating an instance of the Rectangle class
rectangle = Rectangle(4, 6)

 

# Calling the methods defined in the interface
print(rectangle.area())          # Output: 24
print(rectangle.perimeter())      # Output: 20

In the above example, the Shape class is an abstract base class that defines the interface for shapes. It specifies two abstract methods, area() and perimeter(), which must be implemented by any class inheriting from Shape. The Rectangle class inherits from Shape and provides concrete implementations for the abstract methods.

Abstraction allows for the creation of generalized and modular code by hiding implementation details and focusing on essential features. It promotes code reusability, maintainability, and scalability by providing a clear separation between the interface and the implementation. Users can interact with objects at a higher level, utilizing the defined behaviors without being concerned with the internal complexities.

Object Lifecycle in Python

The object lifecycle refers to the stages an object goes through during its existence in a Python program. Understanding the object lifecycle is important for managing resources, memory, and ensuring proper initialization and cleanup. In Python, the object lifecycle consists of three main stages: object creation, object usage, and object destruction.

Object Creation:

Example:

class Car:
     def __init__(self, make, model):
          self.make = make
          self.model = model

 

# Creating an instance of the Car class
car = Car(“Toyota”, “Camry”)

In the example above, the Car class has an __init__() method that initializes the make and model attributes of the car object. When Car(“Toyota”, “Camry”) is called, a new instance of the Car class is created, and the __init__() method is executed to initialize the attributes.

Object Usage:

Example:

class Car:
     def __init__(self, make, model):
          self.make = make
          self.model = model

     def start_engine(self):
          print(f”The {self.make} {self.model} engine is started.”)

 

# Creating an instance of the Car class
car = Car(“Toyota”, “Camry”)

 

# Accessing attributes and calling methods
print(car.make)         # Output: Toyota
print(car.model)        # Output: Camry
car.start_engine()       # Output: The Toyota Camry engine is started.

In the example above, the Car class has an __init__() method for initialization and a start_engine() method. After creating a car object, you can access its attributes (make and model) and call its methods (start_engine()).

Object Destruction:

Example:

class Car:
     def __init__(self, make, model):
          self.make = make
          self.model = model

    def __del__(self):
         print(f”The {self.make} {self.model} object is being destroyed.”)

 

# Creating and destroying an instance of the Car class
car = Car(“Toyota”, “Camry”)
del car # Explicitly deleting the car object

In the example above, the Car class has an __del__() method that is automatically called before the car object is destroyed. When the del statement is used to explicitly delete the car object, the __del__() method is invoked to perform any necessary cleanup actions.

Understanding the object lifecycle helps in managing resources efficiently, avoiding memory leaks, and ensuring proper initialization and cleanup of objects in Python programs. By controlling object creation, usage, and destruction, you can effectively manage the lifecycle of objects and optimize the overall program execution.

Class Variables and Instance Variables in Python:

In Python, variables in a class can be classified as class variables and instance variables, each with its own scope and usage within the class.

Class Variables:

Class variables are variables that are shared among all instances of a class.
They are defined within the class but outside of any instance methods.
Class variables have the same value for all instances of the class.

Example:

Class Car:
      # Class variable
      wheels = 4

      def __init__(self, make, model):
          # Instance variables
          self.make = make
          self.model = model

 

# Accessing class variable
print(Car.wheels)           # Output: 4

 

# Creating instances of the Car class
car1 = Car(“Toyota”, “Camry”)
car2 = Car(“Honda”, “Accord”)

 

# Accessing class variable through instances
print(car1.wheels)          # Output: 4
print(car2.wheels)            # Output: 4

In the above example, the wheels variable is a class variable defined within the Car class. It is shared among all instances of the class and can be accessed using the class name (Car.wheels) or through instances of the class (car1.wheels, car2.wheels).

Instance Variables:

Example:

class Car:
     def __init__(self, make, model):
         # Instance variables
         self.make = make
         self.model = model

 

# Creating instances of the Car class
car1 = Car(“Toyota”, “Camry”)
car2 = Car(“Honda”, “Accord”)

 

# Accessing and modifying instance variables
print(car1.make)          # Output: Toyota
print(car2.model)         # Output: Accord

 

car1.make = “Ford”
car2.model = “Civic”

 

print(car1.make)       # Output: Ford
print(car2.model)      # Output: Civic

In the above example, make and model are instance variables defined within the __init__() method of the Car class. Each instance of the class (car1, car2) has its own copy of these instance variables, and they can be accessed and modified independently.

Understanding the distinction between class variables and instance variables is important for proper data encapsulation and managing the state of objects in Python classes. Class variables allow for shared data among all instances, while instance variables hold unique data specific to each instance.


Unit 5: Graphics

Graphics in Python

Graphics play a vital role in creating engaging and visually appealing applications. Python provides several libraries and frameworks that enable developers to create stunning visualizations, 2D and 3D animations, and interactive graphical user interfaces (GUIs). In this chapter, we will explore some of the popular libraries and techniques for graphics in Python, including Matplotlib for data visualization and Tkinter for GUI development.

Introduction to Matplotlib:

Matplotlib is a widely used library for creating 2D plots and graphs in Python. It provides a high-level interface for creating static, animated, and interactive visualizations. To get started with Matplotlib, you’ll need to install it using pip:

pip install matplotlib

Once installed, you can import the library in your Python script or interactive shell:

import matplotlib.pyplot as plt

Creating Basic Plots with Matplotlib:

Matplotlib provides various functions to create different types of plots, such as line plots, scatter plots, bar plots, and more. Let’s start by creating a simple line plot:

import matplotlib.pyplot as plt

 

# Data
x = [1, 2, 3, 4, 5]
y = [2, 4, 6, 8, 10]

 

# Create a figure and axis
fig, ax = plt.subplots()

 

# Plot the data
ax.plot(x, y)

 

# Add labels and title
ax.set_xlabel(‘X-axis’)
ax.set_ylabel(‘Y-axis’)
ax.set_title(‘Line Plot’)

 

# Show the plot
plt.show()

In the above code, we create a figure and axis using plt.subplots(), plot the data using ax.plot(), and add labels and a title to the plot. Finally, we call plt.show() to display the plot.

Customizing Plots with Matplotlib:

Matplotlib provides a wide range of options for customizing plots. You can modify the line style, marker style, colors, legends, gridlines, and much more. Let’s customize our line plot:

import matplotlib.pyplot as plt

# Data
x = [1, 2, 3, 4, 5]
y = [2, 4, 6, 8, 10]

 

# Create a figure and axis
fig, ax = plt.subplots()

# Plot the data with customizations

ax.plot(x, y, linestyle=’–‘, marker=’o’, color=’r’, label=’Data’)

 

# Add labels and title
ax.set_xlabel(‘X-axis’)
ax.set_ylabel(‘Y-axis’)
ax.set_title(‘Customized Line Plot’)

 

# Add a legend
ax.legend()

 

# Show the plot
plt.show()

In this example, we use additional arguments in the ax.plot() function to customize the line style (linestyle=’–‘), marker style (marker=’o’), and color (color=’r’). We also add a legend to the plot using ax.legend().

Introduction to Tkinter:

Tkinter is a built-in Python library for creating GUI applications. It provides a set of tools and widgets to build windows, dialogs, buttons, textboxes, and more. To use Tkinter, no installation is required as it comes bundled with Python.

import tkinter as tk

 

# Create the main window

window = tk.Tk()

 

# Set the window title

window.title(“My GUI Application”)

 

# Add widgets and functionality

# Start the main event loop
window.mainloop()

In the above code, we import tkinter as tk, create the main window using tk.Tk(), set the window title, add widgets and functionality, and start the main event loop using window.mainloop().

Creating a Simple GUI with Tkinter:

Let’s create a simple GUI application with a button using Tkinter:

import tkinter as tk

 

# Function to handle button click
def button_click():
print(“Button clicked!”)

 

# Create the main window
window = tk.Tk()

 

# Set the window title
window.title(“My GUI Application”)

 

# Create a button widget
button = tk.Button(window, text=”Click Me”, command=button_click)

 

# Add the button to the window
button.pack()

 

# Start the main event loop
window.mainloop()

In this example, we define a function button_click() that will be called when the button is clicked. We create a button widget using tk.Button() and specify the function to call when the button is clicked using the command parameter. We then add the button to the window using button.pack().

Some Graphics Examples:

Some more examples of programs that utilize different graphics libraries in Python:

Matplotlib Line Plot:

import matplotlib.pyplot as plt

 

# Data
x = [1, 2, 3, 4, 5]
y = [2, 4, 6, 8, 10]

 

# Create a figure and axis
fig, ax = plt.subplots()

 

# Plot the data
ax.plot(x, y)

 

# Add labels and title
ax.set_xlabel(‘X-axis’)
ax.set_ylabel(‘Y-axis’)
ax.set_title(‘Line Plot’)

 

# Show the plot
plt.show()

Seaborn Scatter Plot:

Seaborn is a statistical data visualization library based on Matplotlib. It provides a high-level interface for creating visually appealing and informative statistical graphics. Seaborn simplifies the process of creating complex statistical visualizations by offering built-in themes and functions for common statistical plots.

import seaborn as sns

 

# Data
x = [1, 2, 3, 4, 5]
y = [2, 4, 6, 8, 10]

 

# Create a scatter plot
sns.scatterplot(x, y)

 

# Show the plot
plt.show()

Plotly 3D Scatter Plot:

Plotly is a powerful library for creating interactive and web-based visualizations in Python. It provides a wide range of visualization types, including 2D and 3D plots, statistical charts, maps, and more. Plotly allows for interactivity, such as zooming, panning, and hovering over data points.

import plotly.graph_objects as go

 

# Data
x = [1, 2, 3, 4, 5]
y = [2, 4, 6, 8, 10]
z = [5, 3, 2, 4, 6]

 

# Create a 3D scatter plot
fig = go.Figure(data=[go.Scatter3d(x=x, y=y, z=z, mode=’markers’)])

 

# Show the plot
fig.show()

Bokeh Bar Plot:

Bokeh is a Python library for creating interactive visualizations for the web. It provides tools for creating interactive plots, dashboards, and data applications. Bokeh supports a variety of plot types, including scatter plots, line plots, bar plots, and more.

from bokeh.plotting import figure, show

 

# Data
x = [‘A’, ‘B’, ‘C’, ‘D’]
y = [10, 15, 7, 12]

 

# Create a bar plot
p = figure(x_range=x)

p.vbar(x=x, top=y, width=0.5)

 

# Show the plot
show(p)

Pygame Animation:

Pygame is a popular library for creating games and interactive graphical applications in Python. It provides functionality for handling graphics, sound, and user input. Pygame allows developers to create interactive and animated applications with ease.

import pygame
import random

 

# Initialize Pygame
pygame.init()

 

# Set up the display
screen = pygame.display.set_mode((800, 600))

 

# Game loop
running = True
while running:
        for event in pygame.event.get():
              if event.type == pygame.QUIT:
              running = False

 

       # Fill the screen with a random color
       screen.fill((random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))

 

      # Update the display
      pygame.display.flip()

 

# Quit Pygame
pygame.quit()

Exit mobile version