Skip to content

Session 2#

Here’s a detailed explanation of each topic with step-by-step breakdowns and examples.


1. Boolean Expressions and Logical Operators#

Boolean Expressions are expressions that evaluate to either True or False.

  • Python uses True and False as boolean values.
  • Comparisons like ==, !=, <, >, <=, and >= are used to create boolean expressions.

Comparison Operators:#

Operator Description Example Result
== Equals 5 == 5 True
!= Not equal 5 != 3 True
< Less than 3 < 5 True
> Greater than 7 > 2 True
<= Less than or equal to 5 <= 5 True
>= Greater than or equal to 6 >= 7 False

Logical Operators:#

Logical operators combine multiple boolean expressions.

Operator Description Example Result
and True if both true True and False False
or True if at least one true True or False True
not Reverses the value not True False

Example Code:#

# Using comparison operators
x = 15
y = 10

# Boolean expressions
print(x > y)       # True
print(x == y)      # False

# Logical operators
print(x > 5 and y < 15)   # True
print(x < 5 or y < 15)    # True
print(not (x > y))        # False

2. Conditional and Alternative Execution#

Conditional execution allows code to run only when certain conditions are met using if, else, and elif.

Syntax:#

if condition:
    # Code block executed if condition is True
elif another_condition:
    # Code block executed if the first condition is False but this one is True
else:
    # Code block executed if all conditions are False

Example 1: Basic if Statement#

number = 10

if number > 5:
    print("The number is greater than 5.")

Example 2: if-else Statement#

number = 3

if number % 2 == 0:
    print("Even number")
else:
    print("Odd number")

3. Chained and Nested Execution#

Chained Execution:#

Allows checking multiple conditions using elif. Useful when conditions are mutually exclusive.

score = 85

if score >= 90:
    print("Grade: A")
elif score >= 80:
    print("Grade: B")
elif score >= 70:
    print("Grade: C")
else:
    print("Grade: F")

Nested Execution:#

One condition is checked inside another. Useful for hierarchical or dependent conditions.

age = 25

if age > 18:
    if age < 30:
        print("You are a young adult.")
    else:
        print("You are an adult.")
else:
    print("You are a minor.")

4. Catching Exceptions with try and except#

Errors can occur during execution, such as dividing by zero or providing invalid input.

Python handles these errors using try and except blocks.

Why Use Exception Handling?#

  • Prevents program crashes.
  • Provides meaningful error messages.
  • Handles unexpected issues gracefully.

Syntax:#

try:
    # Code that may raise an exception
except ExceptionType:
    # Code that runs if that exception occurs
else:
    # Code that runs if no exception occurs
finally:
    # Code that runs no matter what

Example 1: Handling a ValueError#

try:
    number = int(input("Enter a number: "))
    print(f"You entered {number}")
except ValueError:
    print("That's not a valid number!")

Example 2: Handling Multiple Exceptions#

try:
    x = int(input("Enter numerator: "))
    y = int(input("Enter denominator: "))
    result = x / y
    print(f"Result: {result}")
except ValueError:
    print("Invalid input! Please enter integers.")
except ZeroDivisionError:
    print("Cannot divide by zero.")

Practice Exercises#

1. Boolean Expressions#

Write a function that takes two numbers and returns:

  • "Both positive" if both numbers are positive.
  • "One is positive" if only one is positive.
  • "None are positive" otherwise.

2. Conditional Execution#

Create a program that categorizes a number as:

  • "Negative"
  • "Zero"
  • "Positive and even"
  • "Positive and odd"

3. Nested Execution#

Build a program that:

  • Prompts the user for their age.
  • If age < 18: "Minor".
  • If 18 <= age < 60: "Adult".
  • Else: Check if the person is retired or working.

4. Exception Handling#

Write a calculator that:

  • Accepts two numbers.
  • Asks the user for an operation (+, ,, /).
  • Handles invalid operations and division by zero.

Would you like help implementing any of these exercises? 😊


Exceptions#

Exception handling in Python allows you to gracefully handle errors during runtime without crashing the program. Here's a guide to understanding and using exception handling effectively:


1. What are Exceptions?#

Exceptions are errors that occur during the execution of a program, disrupting its flow. Common examples include:

  • ZeroDivisionError: Dividing by zero.
  • FileNotFoundError: File not found.
  • ValueError: Invalid value provided.
  • TypeError: Incorrect type used.

2. Basic Exception Handling: try and except#

Use try and except blocks to handle exceptions.

Example:#

try:
    num = int(input("Enter a number: "))
    result = 10 / num
    print("Result:", result)
except ZeroDivisionError:
    print("You cannot divide by zero!")
except ValueError:
    print("Invalid input! Please enter a valid number.")

3. Handling Multiple Exceptions#

You can handle multiple exceptions separately or together.

Separate Handling#

try:
    data = int(input("Enter a number: "))
    result = 10 / data
except ValueError:
    print("That's not a number!")
except ZeroDivisionError:
    print("Division by zero is not allowed!")

Catching Multiple Exceptions in One Block#

try:
    data = int(input("Enter a number: "))
    result = 10 / data
except (ValueError, ZeroDivisionError) as e:
    print(f"An error occurred: {e}")

4. Catching All Exceptions#

Use except Exception to handle any exception, but avoid overusing it since it can mask unexpected errors.

try:
    # Code that may raise an exception
    print(10 / 0)
except Exception as e:
    print(f"An unexpected error occurred: {e}")

5. Using else#

The else block runs if no exceptions are raised in the try block.

try:
    num = int(input("Enter a number: "))
    result = 10 / num
except ZeroDivisionError:
    print("You cannot divide by zero!")
except ValueError:
    print("Invalid input!")
else:
    print("No errors! The result is:", result)

6. Using finally#

The finally block runs regardless of whether an exception occurs or not, making it ideal for cleanup tasks like closing files.

try:
    file = open("example.txt", "r")
    content = file.read()
    print(content)
except FileNotFoundError:
    print("File not found!")
finally:
    print("Closing the file.")
    file.close()

7. Raising Exceptions#

Use raise to trigger exceptions manually.

x = -5
if x < 0:
    raise ValueError("Negative values are not allowed!")

8. Custom Exceptions#

You can define your own exceptions by creating a custom class that inherits from Exception.

class NegativeNumberError(Exception):
    pass

try:
    num = int(input("Enter a positive number: "))
    if num < 0:
        raise NegativeNumberError("Negative numbers are not allowed!")
except NegativeNumberError as e:
    print(e)

9. Nested Exception Handling#

You can nest try-except blocks for more precise error handling.

try:
    num = int(input("Enter a number: "))
    try:
        result = 10 / num
    except ZeroDivisionError:
        print("Inner error: Division by zero!")
except ValueError:
    print("Outer error: Invalid input!")

10. Best Practices#

  1. Be Specific: Catch specific exceptions to avoid unintended behavior.
  2. Avoid Bare except: Using just except: without specifying the exception type can hide real issues.
  3. Use finally: Ensure cleanup tasks are performed, such as closing files or releasing resources.
  4. Log Errors: Use logging to record exceptions instead of just printing them.
  5. Re-Raise Exceptions: If necessary, re-raise an exception after handling it.

Example: Logging Errors#

import logging

logging.basicConfig(filename="errors.log", level=logging.ERROR)

try:
    result = 10 / 0
except ZeroDivisionError as e:
    logging.error("ZeroDivisionError occurred: %s", e)
    raise  # Re-raise the exception

Practice Problems#

  1. Write a program that handles a FileNotFoundError when trying to read a file.
  2. Create a program that catches a ValueError when converting input to an integer.
  3. Define a custom exception for invalid user ages (e.g., negative or above 150).

Would you like a detailed explanation of any specific part?


Iterative Operations#

Iteration in Python allows you to loop through data structures, execute repetitive tasks, and manipulate collections of data. Here's an overview of how to work with iterations:


1. for Loop#

A for loop is used to iterate over a sequence (like lists, tuples, strings, or ranges).

Example 1: Iterating over a list#

fruits = ['apple', 'banana', 'cherry']
for fruit in fruits:
    print(fruit)

Example 2: Using range()#

The range() function generates a sequence of numbers.

for i in range(5):  # 0 to 4
    print(i)

# Specifying start, stop, and step
for i in range(1, 10, 2):  # 1, 3, 5, 7, 9
    print(i)

Example 3: Iterating over a string#

word = "Python"
for char in word:
    print(char)

2. while Loop#

A while loop runs as long as a condition is True.

Example 1: Basic Usage#

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

Example 2: Infinite Loop#

while True:
    print("This will run forever!")  # Use Ctrl+C to stop
    break  # Exit the loop

3. Iterating with enumerate()#

Use enumerate() to get both the index and value when iterating over a sequence.

names = ['Alice', 'Bob', 'Charlie']
for index, name in enumerate(names):
    print(f"{index}: {name}")

4. Iterating with zip()#

The zip() function pairs elements from two or more iterables.

names = ['Alice', 'Bob', 'Charlie']
ages = [25, 30, 35]
for name, age in zip(names, ages):
    print(f"{name} is {age} years old.")

5. Iterating with Dictionaries#

Use .items(), .keys(), or .values() for dictionary iteration.

Example 1: Iterating over keys and values#

person = {'name': 'Alice', 'age': 25, 'city': 'New York'}
for key, value in person.items():
    print(f"{key}: {value}")

Example 2: Iterating over keys only#

for key in person.keys():
    print(key)

6. List, Dictionary, and Set Comprehensions#

Comprehensions provide a concise way to create or manipulate collections.

Example 1: List comprehension#

numbers = [1, 2, 3, 4, 5]
squared = [x ** 2 for x in numbers]
print(squared)  # [1, 4, 9, 16, 25]

Example 2: Dictionary comprehension#

numbers = [1, 2, 3, 4, 5]
squared_dict = {x: x ** 2 for x in numbers}
print(squared_dict)  # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

Example 3: Set comprehension#

numbers = [1, 2, 2, 3, 4, 4, 5]
unique_squares = {x ** 2 for x in numbers}
print(unique_squares)  # {1, 4, 9, 16, 25}

7. Itertools Module#

The itertools module provides advanced tools for iteration.

Example: Using itertools to create combinations#

import itertools

data = ['A', 'B', 'C']
combinations = itertools.combinations(data, 2)
for combo in combinations:
    print(combo)  # ('A', 'B'), ('A', 'C'), ('B', 'C')

8. Loop Control Statements#

  1. break: Exit the loop immediately.
  2. continue: Skip the current iteration and move to the next.
  3. pass: Do nothing (useful as a placeholder).

Example#

for i in range(5):
    if i == 3:
        break  # Exit the loop when i is 3
    elif i == 2:
        continue  # Skip when i is 2
    else:
        pass  # Do nothing
    print(i)  # 0, 1, 4

9. Custom Iterators#

Create custom iterable objects by defining the __iter__() and __next__() methods.

class MyNumbers:
    def __iter__(self):
        self.num = 1
        return self

    def __next__(self):
        if self.num <= 5:
            result = self.num
            self.num += 1
            return result
        else:
            raise StopIteration

my_iter = MyNumbers()
for number in my_iter:
    print(number)  # 1, 2, 3, 4, 5

Practice Problems#

  1. Write a program to print all even numbers in a list.
  2. Create a loop that stops when a specific number is found in a sequence.
  3. Use zip() to merge two lists into a dictionary.

Would you like examples for any specific case?