Pyhton
Pyhton
Pyhton
• Interactive
You type directly to Python one line at a time and it responds
• Script
You enter a sequence of statements (lines) into a file using a text
editor and tell Python to execute the statements in the file
1. Logical Operators
1.1 Operators
Key Points:
Python is a high-level programming language used for writing human-readable code
that is translated into binary for the computer to execute.
Arithmetic operations like addition, subtraction, multiplication, and division are
intuitive and easy to perform.
- Subtraction 10 - 4 6
* Multiplication 7*2 14
** Exponentiation 2 ** 3 8
B. Notes:
Division ( / ) always results in a float.
Use // for integer division.
Parentheses and BODMAS rules determine operation precedence.
Avoid leaving operators without operands (e.g., 8+ → Error).
Short-hand Assignment:
x=5
x += 3 # Equivalent to x = x + 3
print(x) # Output: 8
a, b = 10, 20
print(a, b) # Output: 10 20
Example:
a=5
b = 10
Truth Table:
A B A and B A or B not A
True True True True False
True False False True False
False True False True True
False False False False True
Example:
a = 10
b = 20
Example:
a = 10
print(-a) # Output: -10
1.6 Precedence and Associativity of Operators
Precedence: Determines the order in which operators are evaluated.
Associativity: Determines the order of evaluation when operators have the same
precedence.
Operator Description
(X*Y) Parenthesis
** Exponentiation
*, /, //, % Multiplication, Division, Modulus
+, - Addition, Subtraction
<, >, <=, >= Relational Operators
==, != Equality Operators
not Logical NOT
and Logical AND
or Logical OR
Example:
result = 10 + 2 * 3 ** 2
print(result) # Output: 28 (Exponentiation > Multiplication > Addition)
2. Math in Python
2.1 Importing the Math Module
To use the math module, you must import it first:
Examples:
01. Direct Import
import math
print(math.sqrt(25)) # Output: 5.0
import math as m
print(m.sqrt(25)) # Output: 5.0
3. Strings in Python
3.1 What Are Strings?
A string is a sequence of characters enclosed in single ( ' ) or double ( " ) quotes.
Escape sequences like backslashes ( \ ) allow you to use special characters within
strings.
Examples:
Repetition:
Repeats a string using the * operator.
Notes:
Strings in Python are enclosed in either single ( ' ) or double ( " ) quotation marks.
Examples:
"shiva"
'shiva'
variable_name = "string_value"
Example:
name = 'shiva'
name # Output: 'shiva'
Use the slice syntax with start index and end index separated by a colon ( : ). The end
index is not included.
name = 'youtube'
name[2:5] # Output: 'utu'
If the start or end index is omitted, the slice starts from the beginning or goes to the end
of the string, respectively.
print(s[:2]) # Output: Mo
print(s[8:]) # Output: thon
print(s[:]) # Output: Monty Python
The in keyword checks if a substring exists within a string and returns a Boolean value.
fruit = 'banana'
print('n' in fruit) # Output: True
print('m' in fruit) # Output: False
if 'a' in fruit:
print('Found it!') # Output: Found it!
4. Index Error:
zot = 'abc'
print(zot[5]) # IndexError: string index out of range
5. String Length
The len() function returns the length of a string.
fruit = 'banana'
print(len(fruit)) # Output: 6
Individual characters in a string can be accessed using an index in square brackets. The
index starts at 0 and can be an expression.
fruit = 'banana'
letter = fruit[1]
print(letter) # Output: a
x=3
w = fruit[x - 1]
print(w) # Output: n
Indexing youtube
Positive 0123456
Indexing youtube
Negative -7 -6 -5 -4 -3 -2 -1
Single character:
a = 'Hello'
b = a + 'There'
print(b) # Output: HelloThere
---
## 3.8 String Library Methods in Python
Here’s a **table of string methods** that were mentioned in the chapter, along with their
descriptions and examples:
Based on the content of the chapter and the image you provided, here’s a **comprehensive list of
essential string methods** that were mentioned in the chapter, including the ones from the image
and additional ones like `len()`:
### **Essential String Methods**
| **Method** | **Description** | **Example**
|
| ---------------------------------------- | ---------------------------------------------------------------------------------
------------------------- | ------------------------------------------------- |
| **`len()`** | Returns the length of the string. |
`len("banana")` → `6` |
| **`str.capitalize()`** | Converts the first character of the string to uppercase.
| `"hello".capitalize()` → `"Hello"` |
| **`str.replace(old, new, count)`** | Replaces all occurrences of `old` with `new`. Optionally, only
the first `count` occurrences are replaced. | `"hello".replace("l", "x")` → `"hexxo"` |
| **`str.lower()`** | Converts the string to lowercase. |
`"Hello".lower()` → `"hello"` |
| **`str.upper()`** | Converts the string to uppercase. |
`"Hello".upper()` → `"HELLO"` |
| **`str.endswith(suffix, start, end)`** | Checks if the string ends with `suffix`. Optionally, specify a
start and end index. | `"hello".endswith("lo")` → `True` |
| **`str.startswith(prefix, start, end)`** | Checks if the string starts with `prefix`. Optionally, specify a
start and end index. | `"hello".startswith("he")` → `True` |
| **`str.find(sub, start, end)`** | Returns the index of the first occurrence of `sub`. Returns `-1` if
not found. | `"banana".find("na")` → `2` |
| **`str.lstrip(chars)`** | Removes leading (left) whitespace or specified characters.
| `" hello ".lstrip()` → `"hello "` |
| **`str.rstrip(chars)`** | Removes trailing (right) whitespace or specified characters.
| `" hello ".rstrip()` → `" hello"` |
| **`str.strip(chars)`** | Removes both leading and trailing whitespace or specified characters.
| `" hello ".strip()` → `"hello"` |
| **`str.split(sep, maxsplit)`** | Splits the string into a list based on the separator `sep`.
Optionally, limit the number of splits. | `"hello,world".split(",")` → `["hello", "world"]` |
```python
word1 = "apple"
word2 = "apple"
word3 = "banana"
word1 = "apple"
word2 = "banana"
word3 = "apricot"
print(word1 < word2) # Output: True (because 'apple' comes before 'banana')
print(word1 < word3) # Output: True (because 'apple' comes before 'apricot')
print(word2 < word3) # Output: False (because 'banana' comes after 'apricot')
word1 = "banana"
word2 = "apple"
word3 = "apricot"
print(word1 > word2) # Output: True (because 'banana' comes after 'apple')
print(word1 > word3) # Output: True (because 'banana' comes after 'apricot')
print(word2 > word3) # Output: False (because 'apple' comes before 'apricot')
word1 = "Apple"
word2 = "apple"
word3 = "Banana"
print(word1 < word2) # Output: True (because 'Apple' comes before 'apple')
print(word1 < word3) # Output: True (because 'Apple' comes before 'Banana')
print(word2 < word3) # Output: False (because 'apple' comes after 'Banana')
word1 = "apple"
word2 = "banana"
word3 = "cherry"
print(word1 < word2 < word3) # Output: True (because 'apple' < 'banana' < 'cherry')
print(word1 < word3 < word2) # Output: False (because 'apple' < 'cherry' is True, but 'cherry' <
'banana' is False)
word1 = "apple"
word2 = "applet"
word3 = "app"
print(word1 < word2) # Output: True (because 'apple' is shorter and comes before 'applet')
print(word1 < word3) # Output: False (because 'apple' comes after 'app')
print(word2 < word3) # Output: False (because 'applet' comes after 'app')
word1 = "apple"
word2 = "apple"
word3 = "banana"
word1 = "100"
word2 = "99"
word3 = "101"
print(word1 < word2) # Output: True (because '1' < '9' in Unicode)
print(word1 < word3) # Output: True (because '100' is compared character by character)
print(word2 < word3) # Output: True (because '99' is compared character by character)
word1 = "apple!"
word2 = "apple"
word3 = "apple#"
print(word1 < word2) # Output: False (because '!' has a higher Unicode value than the end of
'apple')
print(word1 < word3) # Output: True (because '!' comes before '#')
word1 = ""
word2 = "apple"
print(word1 < word2) # Output: True (because an empty string is less than any non-empty string)
print(word1 > word2) # Output: False
Examples:
Good: spam , eggs , spam23 , _speed
Bad: 23spam , #sign , var.12
Different: spam , Spam , SPAM
a = 42
print(id(a)) # Outputs the memory address of the variable `a`
a = 42
b=a
print(id(a), id(b)) # Both variables point to the same memory address
a = 42
a = 50 # `a` now points to a new memory location
x = [1, 2, 3]
x = None # The previous list [1, 2, 3] is eligible for garbage collection
4.5 Constants
Constants are the Fixed values such as numbers, letters, and strings, are called “constants”
because their value does not change
print(123)
# output : 123
print(98.6)
# output : 98.6
print('Hello world')
# output : Hello world
PI = 3.14159
GRAVITY = 9.8
a=5
print(type(a)) # Output: <class 'int'>
1. None Type
a = None
print(type(a)) # Output: <class 'NoneType'>
2. Numbers
x = 10
Float ( float ): Decimal numbers.
Example:
y = 3.14
3. Booleans
is_valid = True
5. Sets
6. Dictionary
Collection of key-value pairs.
Example:
a = 5.6
b = int(a) # Converts float to int
c = str(b) # Converts int to string
4.11 Literals
Literals are constant values assigned to constant variables.
Example
Important Note
The input() function always returns a string. To perform operations like arithmetic, you
need to convert the input using type-casting functions such as int() or float() .
Examples
Example
If the user enters 3 + 4 * 2 , the eval() function calculates the result and returns 11 .
Important Note
Be cautious when using eval() with untrusted input, as it can execute arbitrary code and
pose a security risk.
How It Works
import sys
python3 MyCode.py 5 10
Output:
Sum: 15
B. Key Features:
union() Returns a new set with elements from both sets. set1.union(set2)
Built-in Functions:
min() : Returns the smallest value.
Example: min([10, 20, 30]) → 10
max() : Returns the largest value.
Example: max([10, 20, 30]) → 30
sum() : Returns the sum of all values.
Example: sum([10, 20, 30]) → 60
B. Key Features:
Use tuples when you need a collection of values that should not change.
Tuples are faster to iterate than lists.
my_set = {1, 2, 2, 3}
print(my_set) # Output: {1, 2, 3}
02. No Indexing:
Sets do not support indexing or slicing.
03. Unordered:
The order of elements is not guaranteed.
B. Key Features:
my_dict['Charlie'] = 'Python'
del my_dict['Bob']
D. Nested Dictionaries:
Dictionaries can contain other dictionaries.
Example:
nested_dict = {
'Python': ['PyCharm', 'VS Code'],
'Java': {'JSE': 'NetBeans', 'JEE': 'Eclipse'}
}
nested_dict['Java']['JEE'] # Output: 'Eclipse'
Syntax:
if condition:
statement
How It Works:
Example:
age = 18
if age >= 18:
print("You are eligible to vote.")
In this case, since the condition ( age >= 18 ) is true, the message "You are eligible to vote."
will be printed.
Example:
x=5
if x > 3:
print("x is greater than 3") # This statement is part of the if block
How It Works:
Syntax:
if condition:
statement1
else:
statement2
Example:
age = 16
if age >= 18:
print("You are eligible to vote.")
else:
print("You are not eligible to vote.")
Since the condition age >= 18 is false, the else block will execute, printing "You are not
eligible to vote."
Syntax:
if condition1:
if condition2:
statement1
else:
statement2
else:
statement3
Example:
x = 10
y=5
if x > 5:
if y > 3:
print("Both conditions are true.")
else:
print("The second condition is false.")
else:
print("The first condition is false.")
If x > 5 is true, Python will check the second condition y > 3 and print the appropriate
message based on the result.
7. 5 if , elif , and else Statements
elif stands for "else if," and it is used to check multiple conditions in a chain. You can use if ,
elif , and else to check different conditions and execute different blocks of code based on
the first condition that evaluates to true.
Syntax:
if condition1:
statement1
elif condition2:
statement2
else:
statement3
How It Works:
Example:
x = 15
if x > 20:
print("x is greater than 20")
elif x > 10:
print("x is greater than 10 but less than or equal to 20")
else:
print("x is less than or equal to 10")
In this example:
Since x is 15, the second condition ( x > 10 ) is true, so the message "x is greater than 10
but less than or equal to 20" will be printed.
8. Loops in Python
8.1 For Loop
A for loop is used to iterate over a sequence (such as a list, tuple, string, or range). It is
commonly used when the number of iterations is predetermined or when iterating over
collections of data.
The iterable can be a sequence (e.g., list, tuple, string) or an object that implements the
__iter__ method.
For each iteration, the variable takes the value of the next item in the iterable.
The loop ends when there are no items left to iterate over.
Output:
Iteration: 1
Iteration: 2
Iteration: 3
Iteration: 4
Iteration: 5
text = "Python"
for char in text:
print(char, end=" ")
Output:
Python
while condition:
# Statements to execute
# Update the condition
i=1
while i <= 5:
print("Count:", i)
i += 1
Output:
Count: 1
Count: 2
Count: 3
Count: 4
Count: 5
A. For-Else Example
Output:
1
2
Breaking the loop
In this example, the else block does not execute because the loop is terminated using
break .
B. While-Else Example
i=1
while i <= 5:
if i == 3:
print("Breaking the loop")
break
print(i)
i += 1
else:
print("Loop completed without a break")
Output:
1
2
Breaking the loop
A. break Statement
Exits the loop prematurely when a condition is met.
for i in range(1, 6):
if i == 3:
break
print(i)
Output:
1
2
B. continue Statement
Skips the rest of the code for the current iteration and moves to the next iteration.
Output:
1
2
4
5
C. pass Statement
Does nothing and serves as a placeholder.
Output:
1
2
4
5
Output:
1.Printing a Square
n=4
for i in range(n):
for j in range(n):
print("#", end=" ")
print()
Output:
####
####
####
####
n=5
for i in range(1, n + 1):
for j in range(1, i + 1):
print("*", end=" ")
print()
Output:
*
**
***
****
*****
9. Arrays
9.1 What is an Array?
An array is a data structure that stores multiple elements of the same data type.
Unlike lists, arrays enforce type consistency, meaning all elements must belong to the
same type.
Arrays in Python are dynamic, meaning their size can increase or decrease during
program execution.
To use arrays in Python, you must import the array module:
# Accessing elements
print(my_array[1]) # Output: 2
print(my_array[-1]) # Output: 5
search_element = 3
for index, value in enumerate(my_array):
if value == search_element:
print(f"Element found at index {index}")
break
else:
print("Element not found")
print(my_array.index(3)) # Output: 2
# Create an array
my_array = arr.array('i', [1, 2, 3, 4, 5])
# Add elements
my_array.append(6)
my_array.insert(2, 10)
# Remove elements
my_array.remove(3)
# Print details
print("Array:", my_array)
print("Length:", len(my_array))
print("Buffer Info:", my_array.buffer_info())
10. NumPy
Introduction to NumPy
NumPy (Numerical Python) is a powerful library in Python used for working with arrays,
linear algebra, Fourier transforms, and matrices. It provides high-performance,
multidimensional array objects and tools for working with these arrays efficiently.
1. array()
Creates an array from a list, tuple, or iterable.
You can specify the data type explicitly or let NumPy infer it.
2. linspace()
Generates evenly spaced values over a specified range.
By default, divides the range into 50 parts unless a different number is specified.
Syntax:
3. arange()
Similar to Python’s built-in range() , but returns a NumPy array.
Allows non-integer steps.
Syntax:
4. logspace()
Generates numbers spaced evenly on a logarithmic scale.
Base 10 is used by default.
Syntax:
5. zeros()
Creates an array filled with zeros.
Syntax:
6. ones()
Creates an array filled with ones.
Syntax:
Array Operations
1. Adding Values to Arrays
Perform vectorized operations to add a scalar or another array to an array.
# Add a scalar
arr = arr + 5
print(arr) # Output: [6 7 8 9 10]
3. Aggregation Functions
sum() , min() , and max() can be used to calculate the total, minimum, and maximum
values.
arr2[0] = 10
print(arr1) # Output: [10 2 3]
2. Shallow Copy
Use view() to create a shallow copy.
arr1 = array([1, 2, 3])
arr2 = arr1.view() # Separate memory location
arr2[0] = 10
print(arr1) # Output: [ 1 2 3]
print(arr2) # Output: [10 2 3]
3. Deep Copy
Use copy() to create an independent copy.
arr2[0] = 10
print(arr1) # Output: [1 2 3]
print(arr2) # Output: [10 2 3]
Multidimensional Arrays
1. Two-Dimensional Arrays
A two-dimensional array is an array of arrays.
arr = array([
[1, 2, 3],
[4, 5, 6]
])
2. Reshaping Arrays
Use reshape(r, c) to change the dimensions.
Matrices
Creating Matrices
Convert an array to a matrix using matrix() .
Matrices in NumPy
A matrix is a specialized 2D array where rows and columns are explicitly defined. NumPy
provides the matrix() function to create and manipulate matrices. Matrices are particularly
useful in mathematical computations, especially in linear algebra.
Creating a Matrix
There are several ways to create a matrix in NumPy:
print(zero_matrix)
print(one_matrix)
print(random_matrix)
Attributes of a Matrix
Shape: Returns the dimensions of the matrix as (rows, columns) .
Size: Total number of elements in the matrix.
Transpose: Flips rows and columns using .T .
Data Type: Use .dtype to find the data type of the elements.
from numpy import matrix
Special Matrices
01. Identity Matrix
Use the identity() function to create a square identity matrix.
identity_matrix = identity(3)
print(identity_matrix)
Matrix Functions
Here are some commonly used functions in matrix operations:
Function Description
transpose() Transposes the matrix (flips rows and columns).
sum() Computes the sum of all elements or along a specific axis.
mean() Returns the average of the elements in the matrix.
Function Description
min() Finds the smallest value in the matrix.
max() Finds the largest value in the matrix.
det() Computes the determinant of a square matrix.
inv() Computes the inverse of a square matrix.
dot() Performs matrix multiplication.
eig() Computes the eigenvalues and eigenvectors of a matrix.
# Determinant
print("Determinant:", det(mat))
# Inverse
print("Inverse:\n", inv(mat))
Matrix Reshaping
Reshape a 1D array or matrix into a new shape using reshape() .
Be cautious about matching the total number of elements in the old and new shapes.
# Valid multiplication
result = mat1 * mat2
print(result)
11. Functions
11.1 Introduction to Functions
A function is a block of code designed to perform a specific task.
Functions help in breaking down complex problems into smaller, manageable pieces,
making code more readable, reusable, and easier to debug.
Functions can take input arguments, execute operations, and optionally return values.
def function_name(parameters):
# Function body
return value # Optional
Example:
result = add_numbers(4, 5)
print(result) # Output: 9
Functions must be called to execute. Without calling, they will not run.
11.2 What are Arguments in Python?
Arguments are the values passed to a function when it is called.
Formal Arguments: Variables used in the function definition.
Actual Arguments: Values passed during the function call.
Example:
Example:
def square(x):
return x * x
result = square(4)
print(result) # Output: 16
Immutable Objects (e.g., int , float , str ): Changes inside the function do not affect the
original object.
Mutable Objects (e.g., list , dict ): Changes inside the function affect the original
object.
def update(lst):
print("Before modification:", lst)
lst[1] = 25
print("After modification:", lst)
Explanation:
The list's memory reference is passed to the function, so changes inside the function
modify the original list.
The order of arguments during the function call should match the parameter order in
the definition.
A function can accept an arbitrary number of arguments using *args for positional
arguments and **kwargs for keyword arguments.
Here’s how you can organize your notes for Section 11.5 Scope of Variables based on the
lecture content:
Example:
def func():
x = 10 # Local variable
print(x)
func() # Output: 10
Here, x is a local variable and can only be accessed inside the func() function.
Example:
x = 10 # Global variable
def func():
print(x) # Accessing global variable
func() # Output: 10
Here, x is a global variable and can be accessed inside the func() function.
Example:
x = 10 # Global variable
def func():
global x # Using global keyword
x = 15 # Modifying global variable
print(x)
func() # Output: 15
print(x) # Output: 15 (global variable is modified)
Here, the global keyword ensures that the global variable x is modified inside the
function.
Example:
x = 10 # Global variable
def func():
x = 15 # Local variable
print("Local x:", x) # Output: Local x: 15
func()
print("Updated Global x:", x) # Output: Updated Global x: 20
Here, globals()['x'] is used to access and modify the global variable x inside the
function.
Example:
square = lambda x: x * x
print(square(5)) # Output: 25
add = lambda x, y: x + y
print(add(3, 7)) # Output: 10
Without a condition to terminate the recursion, the program will continue calling itself
indefinitely, eventually leading to a crash.
Python has a default recursion limit of 1000, preventing excessive function calls that
can lead to system crashes.
import sys
sys.setrecursionlimit(2000) # Set the recursion limit to 2000
def factorial(n):
if n == 0:
return 1 # Base case: factorial of 0 is 1
else:
return n * factorial(n - 1) # Recursive call
result = factorial(5)
print(result) # Output: 120
Explanation:
The base case ensures that the recursion stops when n reaches 0 .
Each recursive call computes a smaller factorial until the base case is reached.
import sys
Ensure your recursive function has a clear stopping condition to prevent infinite
recursion.
Recursive functions can be slower and consume more memory compared to iterative
solutions. Use them only when they simplify the logic.
def fibonacci(n):
if n <= 1:
return n # Base cases: fibonacci(0) = 0, fibonacci(1) = 1
else:
return fibonacci(n - 1) + fibonacci(n - 2) # Recursive calls
n=6
for i in range(n):
print(fibonacci(i), end=" ") # Output: 0 1 1 2 3 5
Advantages of Recursion
Simplifies the logic for solving problems with a repetitive structure.
Reduces code complexity for problems like tree traversals or divide-and-conquer
algorithms.
Disadvantages of Recursion
Can lead to stack overflow if not implemented correctly.
Less memory-efficient compared to iterative solutions due to maintaining a call stack.
These occur due to syntax mistakes, like missing colons or improper indentation.
Detected before the code is executed.
Example:
a, b = 5, 10
print("Sum:", a * b) # Incorrect logic (should be addition, not multiplication)
Arise during code execution, typically caused by unforeseen user input or external
factors.
Example:
Try-Except Block
The try block contains code that might raise an error, and the except block handles the
error. This approach ensures the program doesn’t terminate unexpectedly.
Example:
try:
a = 10 / int(input("Enter a number: "))
print("Result:", a)
except ZeroDivisionError:
print("Error: Division by zero is not allowed.")
Example:
try:
a = 10 / 0
except ZeroDivisionError as e:
print("Exception:", e)
Output:
Example:
try:
f = open("example.txt", "r")
# Perform file operations
except FileNotFoundError:
print("File not found.")
finally:
print("Closing file.")
f.close() # Ensures the file is closed
Example:
try:
a = int(input("Enter a number: "))
b = 10 / a
except ZeroDivisionError:
print("Error: Cannot divide by zero.")
except ValueError:
print("Error: Invalid input, please enter a number.")
except Exception as e:
print("An unexpected error occurred:", e)
Use the finally block to release resources like file handles or database connections.
Avoid resource leaks to ensure the stability of your application.
26. Be specific:
Example:
File Handles
A file handle is an object that Python uses to interact with the file. It provides methods for
reading, writing, and closing the file.
fhand = open('mbox.txt')
for line in fhand:
print(line) # Prints each line in the file
fhand = open('mbox.txt')
inp = fhand.read()
print(len(inp)) # Prints the total number of characters in the file
print(inp[:20]) # Prints the first 20 characters
try:
fhand = open('missing.txt')
except FileNotFoundError:
print('File not found!')
2. Writing Files
Opening a File for Writing
To write to a file, open it in write mode ( 'w' ). If the file already exists, its contents will be
overwritten. If it doesn't exist, a new file will be created.
Appending to a File
To add data to an existing file without overwriting it, open the file in append mode ( 'a' ).
fhand = open('mbox.txt')
count = 0
for line in fhand:
count += 1
print('Line Count:', count)
fhand = open('mbox.txt')
for line in fhand:
if not line.startswith('From:'):
continue
print(line)
fhand = open('mbox.txt')
for line in fhand:
if '@uct.ac.za' in line:
print(line)
4. Error Handling
Handling Bad File Names
Always validate user input when dealing with file names to avoid errors.
5. Summary
Operation Description
Opening a File Use open(filename, mode) to open a file. Modes: 'r' (read), 'w'
(write), 'a' (append).
Reading Line by Line Use a for loop to iterate through each line in the file.
Reading the Entire Use read() to read the entire file into a string.
File
Writing to a File Use write() to add data to a file. Always close the file with close() .
Appending to a File Use mode 'a' to add data without overwriting the file.
Counting Lines Use a loop to count the number of lines in a file.
Skipping Lines Use continue to skip lines that don't meet certain criteria.
Searching for Use in to filter lines containing specific substrings.
Substrings
Error Handling Use try-except to handle missing files or invalid input.