0% found this document useful (0 votes)
10 views34 pages

Python Unit II

The document provides an overview of conditional statements (if, if-else, and if-elif-else) and loops (while and for) in Python, explaining their syntax and usage with examples. It also covers comprehensions for creating sequences and functions, detailing how to define, call, and utilize parameters and return values. Key concepts include the differences between while and for loops, as well as advanced function features like default parameters and lambda functions.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views34 pages

Python Unit II

The document provides an overview of conditional statements (if, if-else, and if-elif-else) and loops (while and for) in Python, explaining their syntax and usage with examples. It also covers comprehensions for creating sequences and functions, detailing how to define, call, and utilize parameters and return values. Key concepts include the differences between while and for loops, as well as advanced function features like default parameters and lambda functions.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

UNIT – II

IF CONDITION

The if condition is considered the simplest of the three and makes a decision based on
whether the condition is true or not. If the condition is true, it prints out the indented expression.
If the condition is false, it skips printing the indented expression.

if condition:

expression

Example of if
Suppose you have a variable z, equal to 4. If the value is 'even', you will print z is 'even'.
You will use modulo operator 2, which will return 0 if z is 'even'. As soon as you run the below
code, Python will check if the condition holds. If True, the corresponding code will be executed.

z=4

if z % 2 == 0: # True

print("z is even")

OUTPUT

z is even

Example of multiple lines inside if statement


It is perfectly fine to have more lines inside the if statement, as shown in the below
example. The script will return two lines when you run it. If the condition is not passed, the
expression is not executed.

z=4

if z % 2 == 0:

print("checking" + str(z))

print("z is even")

OUTPUT

checking 4

1
z is even

Example of a False if statement


Let's change the value of z to be odd. You will notice that the code will not print anything
since the condition will not be passed, i.e., False.

z=5

if z % 2 == 0: # False

print("checking " + str(z))

print("z is even")

IF-ELSE CONDITION

The if-else condition adds an additional step in the decision-making process compared to
the simple if statement. The beginning of an if-else statement operates similar to a
simple if statement; however, if the condition is false, instead of printing nothing, the indented
expression under else will be printed.

if condition:

expression

else:

expression

Example of if-else
Continuing our previous example, what if you want to print 'z is odd' when
the if condition is false? In this case, you can simply add another condition, which is
the else condition. If you run it with z equal to 5, the condition is not true, so the expression for
the else statement gets printed out.

z=5

if z % 2 == 0:

print("z is even")

else:

2
print("z is odd")

OUTPUT

z is odd

IF-ELIF-ELSE CONDITION

The most complex of these conditions is the if-elif-else condition. When you run into a
situation where you have several conditions, you can place as many elif conditions as necessary
between the if condition and the else condition.

if condition:

expression

elif condition:

expression

else:

expression

Example one of if-elif-else condition


Below is an example of where you want different printouts for numbers that are divisible
by 2 and 3.

Here, since z equals 3, the first condition is False, so it goes over to the next condition.
The next condition does hold True. Hence, the corresponding print statement is executed.

z=3

if z % 2 == 0:

print("z is divisible by 2")

elif z % 3 == 0:

print("z is divisible by 3")

else:

print("z is neither divisible by 2 nor by 3")

3
OUTPUT

z is divisible by 3

Example two of if-elif-else condition


In the below example, you define two variables room and area. You then construct if-elif-
else and if-else conditions each for room and area, respectively.

In the first condition, you check if you are looking in the kitchen, elif you are looking in
the bedroom, else you are looking around elsewhere. Depending on the value of
the room variable, the satisfied condition is executed.

Similarly, for the area variable, you write an if and else condition and check whether
the area is greater than 15 or not.

# Define variables

room = "bed"

area = 14.0

# if-elif-else construct for room

if room == "kit":

print("Looking around in the kitchen.")

elif room == "bed":

print("Looking around in the bedroom.")

else:

print("Looking around elsewhere.")

# if-elif-else construct for area

if area > 15:

print("Big place!")

else:

4
print("Pretty small.")

When we run the above code, it produces the following result:

Looking around in the bedroom. Pretty small.

REPEAT WITH WHILE LOOP

The while loop is used to repeat a block of code as long as a given condition is True. The
loop continues to run until the condition becomes False.

Syntax:

while condition:
# Block of code to execute while the condition is True

How it works:

 The condition is checked before each iteration.


 If the condition is True, the block of code inside the loop is executed.
 After executing the block, the condition is checked again. If it’s still True, the loop
repeats. If it’s False, the loop exits.

Example: Counting from 1 to 5

count = 1

while count <= 5:


print(count)
count += 1 # Increment count by 1 in each iteration

Explanation:

 The loop starts with count = 1 and checks whether count <= 5.
 If True, it prints the value of count, then increments count by 1.
 This process continues until count becomes 6, at which point the condition count <= 5
becomes False, and the loop exits.

Output:

1
2
3
4
5

5
Example: Using a while loop to sum numbers

n=5
total = 0

while n > 0:
total += n # Add the value of n to total
n -= 1 # Decrease n by 1

print("Sum:", total)

Explanation:

 The loop runs as long as n is greater than 0.


 In each iteration, the value of n is added to total, and n is decremented by 1.
 When n becomes 0, the loop ends, and the program prints the total sum.

Output:

Sum: 15

(In this case, the sum of 5 + 4 + 3 + 2 + 1 = 15.)

ITERATE WITH FOR LOOP

The for loop is used to iterate over a sequence (like a list, tuple, string, or range) and
execute a block of code for each element in that sequence.

Syntax:

for item in sequence:


# Block of code to execute for each item in the sequence

How it works:

 The loop iterates over each item in the sequence.


 For each item, the code inside the loop is executed.
 Once all the items are processed, the loop ends.

Example: Iterating over a list

fruits = ["apple", "banana", "cherry"]

for fruit in fruits:


print(fruit)

6
Explanation:

 The loop iterates through each element in the fruits list.


 In each iteration, the current fruit is printed.

Output:

apple
banana
cherry

Example: Iterating over a string

for letter in "hello":


print(letter)

Explanation:

 The loop iterates through each character in the string "hello".


 In each iteration, the current letter is printed.

Output:

h
e
l
l
o

Comparing while and for Loops

 while loop: Repeats a block of code while a condition is True. It’s useful when the
number of iterations is not known in advance, or the loop depends on a condition that can
change.
 for loop: Iterates over a sequence of elements (such as a list or a range of numbers). It’s
used when you know how many iterations are required.

Example: Using a while loop to repeat a block until a condition is met

n=5

while n > 0:
print(n)

7
n -= 1

Output:

5
4
3
2
1

Example: Using a for loop to iterate over a range

for n in range(5, 0, -1):


print(n)

Output:

5
4
3
2
1

Both examples produce the same output but use different loop constructs.

COMPREHENSIONS IN PYTHON

Comprehensions in Python provide a concise and readable way to create sequences (like
lists, sets, and dictionaries) by iterating over a collection or applying a condition.
Comprehensions reduce the need for loops and are often more efficient and readable.

Types of Comprehensions:

1. List Comprehensions
2. Set Comprehensions
3. Dictionary Comprehensions
4. Generator Expressions

Let’s go through each type step-by-step with examples.

1. List Comprehensions

List comprehensions allow you to create a new list by applying an expression to each
item in an existing iterable (like a list or range).

Syntax:

8
[expression for item in iterable if condition]

 expression: The value to add to the list.


 item: The variable representing each element in the iterable.
 iterable: The sequence (e.g., list, range) you are iterating over.
 if condition: (Optional) A filter that only includes items that meet a certain condition.

Example 1: Basic list comprehension

squares = [x ** 2 for x in range(5)]


print(squares)

Output:

[0, 1, 4, 9, 16]

This creates a list of squares for numbers 0 through 4.

Example 2: List comprehension with a condition

even_numbers = [x for x in range(10) if x % 2 == 0]


print(even_numbers)

Output:

[0, 2, 4, 6, 8]

Here, only even numbers from 0 to 9 are included in the list.

2. Set Comprehensions

Set comprehensions are similar to list comprehensions, but they generate a set instead of a list.
Since sets do not allow duplicates, all values will be unique.

Syntax:

{expression for item in iterable if condition}

Example: Set comprehension

unique_squares = {x ** 2 for x in range(10)}


print(unique_squares)

Output:

Copy code

9
{0, 1, 64, 36, 4, 9, 16, 49, 81, 25}

The output is a set of squares of numbers from 0 to 9, but in no particular order since sets
are unordered.

3. Dictionary Comprehensions

Dictionary comprehensions allow you to create a dictionary by iterating over a sequence


and applying an expression for both keys and values.

Syntax:

{key_expression: value_expression for item in iterable if condition}

Example: Dictionary comprehension

squares_dict = {x: x ** 2 for x in range(5)}


print(squares_dict)

Output:

{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

This creates a dictionary where the keys are numbers, and the values are their squares.

4. Generator Expressions

A generator expression is similar to a list comprehension, but instead of creating a list, it


returns a generator object, which produces items on demand (lazy evaluation). This is useful
when dealing with large datasets to save memory.

Syntax:

(expression for item in iterable if condition)

Example: Generator expression

squares_gen = (x ** 2 for x in range(5))


print(list(squares_gen)) # Convert to list to see the result

Output:

[0, 1, 4, 9, 16]

Here, squares_gen is a generator, and converting it to a list shows the values it generates.

10
Examples of Combined Comprehensions:

Example 1: Nested list comprehension

matrix = [[i * j for j in range(1, 4)] for i in range(1, 4)]


print(matrix)

Output:

[[1, 2, 3], [2, 4, 6], [3, 6, 9]]

This creates a 2D matrix by multiplying row numbers by column numbers.

Example 2: Flattening a list of lists

nested_list = [[1, 2, 3], [4, 5], [6, 7, 8]]


flat_list = [item for sublist in nested_list for item in sublist]
print(flat_list)

Output:

[1, 2, 3, 4, 5, 6, 7, 8]

Comprehensions allow for more concise and readable code when working with sequences,
making it easier to create complex data structures with fewer lines of code. Let me know if you'd
like more detailed examples or have questions!

FUNCTIONS IN PYTHON

Functions in Python allow you to group reusable pieces of code into named blocks. These
blocks can be executed when called, simplifying your code by avoiding repetition. Functions can
take inputs, perform operations, and return results.

Key Concepts of Functions:

1. Defining a Function: Using the def keyword.


2. Calling a Function: Running the function by its name.
3. Parameters: Passing values into a function.
4. Return Statement: Outputting results from a function.
5. Types of Functions: Built-in and user-defined functions.

1. Defining a Function

A function is defined using the def keyword, followed by the function name and
parentheses. The code inside the function runs only when the function is called.

11
Syntax:

def function_name(parameters):
# code block to execute
return result # (optional)

 function_name: The name of the function.


 parameters: Optional. Values you can pass into the function.
 return: Optional. Used to return a value from the function.

Example 1: Simple function definition

def greet():
print("Hello, World!")

This defines a function named greet that prints a message.

2. Calling a Function

You call (or execute) a function by using its name followed by parentheses.

Example 2: Calling a function

greet() # Output: Hello, World!

This runs the greet function, and it prints the message.

3. Parameters in Functions

Functions can take inputs (called parameters) that allow them to operate on different
data. You specify parameters in the parentheses of the function definition.

Example 3: Function with parameters

def greet(name):
print(f"Hello, {name}!")

Example 4: Calling the function with an argument

greet("Alice") # Output: Hello, Alice!


greet("Bob") # Output: Hello, Bob!

Here, name is a parameter, and the function prints a personalized greeting.

4. Return Statement

12
The return statement is used to send a result back from the function. Without return, the
function only performs its actions but does not output a result.

Example 5: Function with a return value

def add(a, b):


return a + b

Example 6: Using the return value

result = add(3, 4)
print(result) # Output: 7

Here, the add function takes two arguments (a and b), adds them, and returns the result.

5. Default Parameters

You can assign default values to parameters. If no argument is passed when calling the
function, the default value will be used.

Example 7: Default parameter

def greet(name="Stranger"):
print(f"Hello, {name}!")

Example 8: Calling with and without argument

greet() # Output: Hello, Stranger!


greet("Alice") # Output: Hello, Alice!

6. Keyword Arguments (kwargs)

You can call functions with keyword arguments, allowing you to specify arguments by
name, not just by position.

Example 9: Using keyword arguments

def introduce(name, age):


print(f"My name is {name} and I am {age} years old.")

Example 10: Keyword argument call

introduce(age=25, name="Alice") # Output: My name is Alice and I am 25 years old.

7. Arbitrary Arguments

13
You can use *args to pass a variable number of positional arguments, and **kwargs to
pass a variable number of keyword arguments to a function.

Example 11: *args for variable number of arguments

python
Copy code
def add_numbers(*args):
return sum(args)

Example 12: Using *args

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

Here, *args allows the function to accept any number of arguments, and they are treated as a
tuple.

8. Lambda Functions

A lambda function is an anonymous, short function defined using the lambda keyword.
It can take any number of arguments but has only one expression.

Syntax:

lambda arguments: expression

Example 13: Lambda function

add = lambda x, y: x + y
print(add(3, 5)) # Output: 8

Here, lambda x, y: x + y is equivalent to a normal function that returns the sum of x and y.

9. Docstrings

You can add documentation to a function using a docstring. This is a string written in the
function body to describe what the function does.

Example 14: Function with docstring

def greet(name):
"""
This function greets the person whose name is passed as an argument.
"""
print(f"Hello, {name}!")

14
To access the docstring, you can use the .__doc__ attribute:

print(greet.__doc__)

Output:

This function greets the person whose name is passed as an argument.

10. Built-in Functions and User-defined Functions

 Built-in functions: These are functions provided by Python, such as print(), len(), type(),
etc.
 User-defined functions: These are functions that you create using the def keyword.

DÉCORATORS IN PYTHON

In Python, decorators are a powerful tool that allow you to modify the behavior of a
function or a class method without changing its actual code. Decorators are functions that wrap
another function to extend or alter its behavior.

Key Concepts:

1. Functions are First-Class Citizens: In Python, functions are treated as first-class objects,
meaning they can be passed as arguments to other functions or returned from other
functions.
2. Higher-Order Functions: A function that accepts another function as an argument or
returns a function is called a higher-order function.
3. Closures: A function that remembers the environment in which it was created, even
when called outside that environment.

1. Basic Structure of a Decorator

A decorator is typically a higher-order function that takes a function as input and returns a new
function.

def decorator_function(original_function):
def wrapper_function():
# Code to modify the behavior of the original function
print("Wrapper function executed before", original_function.__name__)
return original_function()
return wrapper_function

2. Using the @ Syntax for Decorators

In Python, decorators are commonly used with the @decorator_function_name syntax


before the function to be decorated.

15
@decorator_function
def display():
print("Display function executed")

# Calling the decorated function


display()

This is equivalent to:

display = decorator_function(display)

3. Step-by-Step Example of a Decorator

Example: Logging the Execution of a Function

def log_decorator(func):
def wrapper():
print(f"Executing {func.__name__} function...")
result = func() # Call the original function
print(f"Finished executing {func.__name__}")
return result
return wrapper

# Applying the decorator


@log_decorator
def greet():
print("Hello, world!")

# Calling the decorated function


greet()

Output:

Executing greet function...


Hello, world!
Finished executing greet

4. Decorator with Arguments

If the function you want to decorate accepts arguments, the wrapper function inside the
decorator must also accept arguments.

def log_decorator(func):
def wrapper(*args, **kwargs): # Accepting all arguments
print(f"Executing {func.__name__} with arguments {args}, {kwargs}")
result = func(*args, **kwargs)

16
print(f"Finished executing {func.__name__}")
return result
return wrapper

@log_decorator
def greet_user(name, age):
print(f"Hello, {name}. You are {age} years old.")

# Calling the decorated function with arguments


greet_user("Alice", 30)

Output:

Executing greet_user with arguments ('Alice', 30), {}


Hello, Alice. You are 30 years old.
Finished executing greet_user

5. Multiple Decorators

You can apply multiple decorators to a single function. The decorators are applied from
the innermost to the outermost.

python
Copy code
def bold_decorator(func):
def wrapper():
return f"<b>{func()}</b>"
return wrapper

def italic_decorator(func):
def wrapper():
return f"<i>{func()}</i>"
return wrapper

@bold_decorator
@italic_decorator
def say_hello():
return "Hello, world!"

# Calling the function with multiple decorators


print(say_hello())

Output:

<b><i>Hello, world!</i></b>

17
6. Class-Based Decorators

Decorators can also be implemented as classes, which is useful when you want to
maintain state.

class CountCalls:
def __init__(self, func):
[Link] = func
[Link] = 0

def __call__(self, *args, **kwargs):


[Link] += 1
print(f"{[Link].__name__} has been called {[Link]} times")
return [Link](*args, **kwargs)

@CountCalls
def greet():
print("Hello!")

# Calling the decorated function


greet()
greet()

Output:

greet has been called 1 times


Hello!
greet has been called 2 times
Hello!

GENERATOR IN PYTHON

A Generator in Python is a function that returns an iterator using the Yield keyword. In
this article, we will discuss how the generator function works in Python.

Generator Function in Python

A generator function in Python is defined like a normal function, but whenever it


needs to generate a value, it does so with the yield keyword rather than return. If the body of a
def contains yield, the function automatically becomes a Python generator function.

Create a Generator in Python

18
In Python, we can create a generator function by simply using the def keyword and the
yield keyword. The generator has the following syntax in Python:

def function_name():
yield statement

Example:

In this example, we will create a simple generator that will yield three integers. Then
we will print these integers by using Python for loop.

# A generator function that yields 1 for first time,


# 2 second time and 3 third time
def simpleGeneratorFun():
yield 1
yield 2
yield 3

# Driver code to check above generator function


for value in simpleGeneratorFun():
print(value)
Output:
1
2
3

Generator Object

Python Generator functions return a generator object that is iterable, i.e., can be used
as an Iterator. Generator objects are used either by calling the next method of the generator
object or using the generator object in a “for in” loop.

Example:

In this example, we will create a simple generator function in Python to generate


objects using the next() function.

# A Python program to demonstrate use of


# generator object with next()

# A generator function
def simpleGeneratorFun():
yield 1
yield 2
19
yield 3

# x is a generator object
x = simpleGeneratorFun()

# Iterating over the generator object using next

# In Python 3, __next__()
print(next(x))
print(next(x))
print(next(x))
Output:
1
2
3

Example:

In this example, we will create two generators for Fibonacci Numbers, first a simple
generator and second generator using a for loop.

def fib(limit):
a, b = 0, 1
while b < limit:
yield b
a, b = b, a + b

# Create a generator object


x = fib(200)

# Iterate over the generator object and print each value


for i in x:
print(i)

Output
1
1
2
3
5

20
8
13
21
34
55
89
144

1. Fibonacci Numbers:

The Fibonacci sequence is a series of numbers where each number is the sum of the two
preceding ones. It starts with 0 and 1, so:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...

2. Defining the Generator Function fib(limit):

A generator function in Python is defined using def like a normal function, but it contains one or
more yield statements instead of return. Each time yield is called, it provides a value to the caller
and "pauses" the function, retaining its state for the next call.

Code:

def fib(limit):
a, b = 0, 1
while b < limit:
yield b
a, b = b, a + b

 a, b = 0, 1: This initializes the first two Fibonacci numbers, a = 0 and b = 1.


 while b < limit: The loop continues as long as b (the current Fibonacci number) is less
than the specified limit (200 in this case).
 yield b: Instead of returning a value and ending the function, yield provides the value of
b (the current Fibonacci number) and pauses the function.
 a, b = b, a + b: This updates the values of a and b for the next iteration:
o The new value of a becomes the current value of b.
o The new value of b becomes the sum of the previous values of a and b, as per the
Fibonacci rule.

3. Using the Generator:

After defining the function, a generator object is created by calling the function with a limit.

21
x = fib(200)

This creates a generator x, but it doesn't compute anything until it's iterated over.

4. Iterating Over the Generator:

The generator x is then used in a for loop:

for i in x:
print(i)

 The for loop calls the generator function and keeps fetching values yielded by the
function until the while condition b < limit becomes false.
 Each time yield provides a value, it gets printed.
 The function resumes from where it left off after each yield and continues generating the
next Fibonacci number until the loop terminates when b reaches or exceeds 200.

Output:

The Fibonacci sequence generated will be:

Copy code
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144

How It Works:

 The generator function produces one value at a time (due to yield), which makes it
efficient in terms of memory usage.
 Each value is calculated only when needed by the loop, and the function retains its state
between successive calls to yield.

In summary, the generator fib() yields Fibonacci numbers one at a time, and the for loop
fetches and prints each number until the limit (200) is reached.

Python Generator Expression

In Python, generator expression is another way of writing the generator function. It


uses the Python list comprehension technique but instead of storing the elements in a list in
memory, it creates generator objects.

Generator Expression Syntax

22
The generator expression in Python has the following Syntax:
(expression for item in iterable)

Example:

In this example, we will create a generator object that will print the multiples of 5
between the range of 0 to 5 which are also divisible by 2.

# generator expression
generator_exp = (i * 5 for i in range(5) if i%2==0)

for i in generator_exp:
print(i)

Output:
0
10
20

NAMESPACES IN PYTHON

A namespace in Python is a system used to ensure that all the names (identifiers) in a
program are unique. It’s like a container or a mapping between names and objects. Python
maintains different namespaces to avoid name conflicts across a program, so the same variable
name can exist in different namespaces without causing confusion.

Types of Namespaces:

1. Built-in Namespace:
o Contains all the built-in objects, functions, and exceptions in Python, such as
print(), len(), int(), etc.
o This namespace is available by default and can be accessed from anywhere in the
code.

Example:

print("Hello World") # print() is part of the built-in namespace

2. Global Namespace:
o Contains all the names defined at the top level of a module or script (outside of
functions or classes).
o Variables and functions defined in this namespace are globally accessible within
that module.

Example:

23
x = 10 # Global variable

def func():
print(x) # Can access global variable

func() # Output: 10

3. Local Namespace:
o Contains the names defined inside a function or method.
o This namespace is created when the function is called and destroyed when the
function finishes execution.

Example:

def func():
y = 5 # Local variable
print(y)

func() # Output: 5
print(y) # Error: y is not defined outside the function

4. Enclosing Namespace:
o This refers to the namespace of an outer function that wraps an inner function. It
occurs in case of nested functions.
o Inner functions can access variables in the enclosing scope (outer function), but
they cannot modify them without using the nonlocal keyword.

Example:

def outer():
a = 10 # Enclosing variable

def inner():
print(a) # Can access enclosing variable

inner()

outer() # Output: 10

SCOPE IN PYTHON

Scope refers to the region of a program where a variable is accessible. In Python,


variables can have different scopes depending on where they are declared. Python uses the
LEGB rule (Local, Enclosing, Global, Built-in) to resolve variable names.

LEGB Rule:

24
1. Local Scope:
o Variables created inside a function or block are in the local scope.
o They can only be accessed within the function in which they are defined.

Example:

def my_function():
x = 5 # Local scope
print(x)

my_function() # Output: 5

2. Enclosing Scope:
o Variables defined in an outer function’s scope can be accessed in a nested (inner)
function.
o Inner functions can read enclosing variables but need the nonlocal keyword to
modify them.

Example:

def outer_function():
x = 10 # Enclosing scope

def inner_function():
print(x) # Can access enclosing variable

inner_function()

outer_function() # Output: 10

3. Global Scope:
o Variables defined outside any function or class are in the global scope.
o Global variables can be accessed from anywhere in the program, but
modifications from inside functions require the global keyword.

Example:

x = 20 # Global scope

def my_function():
global x
x = 30 # Modify global variable
print(x)

my_function() # Output: 30
print(x) # Output: 30

25
4. Built-in Scope:
o This refers to the scope of built-in names in Python, such as len(), range(), and
int().
o These names are always accessible unless shadowed by local or global variables
with the same name.

Example:

print(len([1, 2, 3])) # Output: 3 (using built-in len function)

Example of LEGB Rule in Action:

x = 50 # Global variable

def outer_function():
x = 30 # Enclosing variable

def inner_function():
x = 20 # Local variable
print(x) # Accesses local variable first

inner_function()

outer_function() # Output: 20
print(x) # Output: 50 (global scope)

 Local Scope: In inner_function(), x = 20 is in the local scope, and it is printed.


 Enclosing Scope: x = 30 in outer_function() is in the enclosing scope, but it’s not
accessed because the local variable x = 20 is used first.
 Global Scope: x = 50 is the global variable and can be accessed globally when not
shadowed by local or enclosing variables.

Modifying Variables in Different Scopes

global Keyword:

To modify a global variable from inside a function, you must declare it using the global
keyword.

Example:

x = 10 # Global variable

def modify_global():
global x # Declare that we're using the global variable
x = 20 # Modify the global variable

26
modify_global()
print(x) # Output: 20

nonlocal Keyword:

To modify a variable in the enclosing (non-global) scope from inside a nested function,
use the nonlocal keyword.

Example:

def outer_function():
x = 10 # Enclosing variable

def inner_function():
nonlocal x # Modify enclosing variable
x = 20

inner_function()
print(x) # Output: 20

outer_function()

HANDLING ERRORS WITH TRY AND EXCEPT BLOCK

In Python, errors or exceptions can be handled using the try and except blocks. The try
block lets you test a block of code for errors, and the except block lets you handle the error if it
occurs. Using try-except helps to prevent the program from crashing due to unhandled
exceptions and allows you to manage errors more gracefully.

1. Basic Syntax

try:
# Code that might cause an error
except:
# Code to handle the error

2. Example of Handling Errors with try and except

If the user enters a zero as the divisor, Python will raise a ZeroDivisionError. We can
handle this error using try and except.

27
try:
numerator = int(input("Enter the numerator: "))
denominator = int(input("Enter the denominator: "))
result = numerator / denominator
print(f"Result: {result}")
except ZeroDivisionError:
print("Error: Division by zero is not allowed.")
except ValueError:
print("Error: Invalid input. Please enter integers only.")

Output (Case 1: Denominator is 0):

Enter the numerator: 10


Enter the denominator: 0
Error: Division by zero is not allowed.

Output (Case 2: Invalid Input):

Enter the numerator: ten


Enter the denominator: 2
Error: Invalid input. Please enter integers only.

3. Handling Multiple Exceptions

You can catch multiple types of exceptions by specifying different except blocks. Each
block can handle a specific type of exception, as shown above.

Example:

try:
number = int(input("Enter a number: "))
print(f"100 divided by {number} is {100 / number}")
except ZeroDivisionError:
print("Error: You cannot divide by zero.")
except ValueError:
print("Error: You must enter an integer.")

4. Using else with try-except

The else block can be used to define code that should run if no exceptions were raised in
the try block.

try:
number = int(input("Enter a number: "))
result = 100 / number
except ZeroDivisionError:

28
print("Error: Division by zero is not allowed.")
except ValueError:
print("Error: Please enter a valid integer.")
else:
print(f"Result: {result}")

Output (If input is valid):

Enter a number: 5
Result: 20.0

5. Using finally Block

The finally block is used to define code that should run no matter what, whether an
exception occurred or not. It is usually used for cleanup actions, like closing a file or releasing
resources.

try:
number = int(input("Enter a number: "))
result = 100 / number
except ZeroDivisionError:
print("Error: Division by zero is not allowed.")
except ValueError:
print("Error: Please enter a valid integer.")
else:
print(f"Result: {result}")
finally:
print("This will always execute, no matter what.")

Output (Case 1: Valid input):

Enter a number: 10
Result: 10.0
This will always execute, no matter what.

Output (Case 2: Error occurs):

Enter a number: 0
Error: Division by zero is not allowed.
This will always execute, no matter what.

6. Raising Exceptions Manually

You can also raise exceptions manually using the raise keyword if certain conditions are not met.

Example:

29
try:
age = int(input("Enter your age: "))
if age < 0:
raise ValueError("Age cannot be negative.")
except ValueError as ve:
print(f"Error: {ve}")

Output (If a negative age is entered):

Enter your age: -5


Error: Age cannot be negative.

Summary:

 The try block is used to write code that may cause an exception.
 The except block is used to handle the exception.
 The else block runs if no exceptions are raised.
 The finally block runs whether or not an exception occurs.
 You can raise exceptions manually using the raise keyword.

USER DEFINED EXCEPTIONS IN PYTHON

User-Defined Exceptions in Python

User-defined exceptions in Python are custom error classes that you create to handle
specific error conditions in your code. They are derived from the built-in Exception class or any
of its sub classes.

User-defined exceptions provide more precise control over error handling in your application −

 Clarity − They provide specific error messages that make it clear what went wrong.
 Granularity − They allow you to handle different error conditions separately.
 Maintainability − They centralize error handling logic, making your code easier to
maintain.

How to Create a User-Defined Exception

To create a user-defined exception, follow these steps −

Step 1 − Define the Exception Class

30
Create a new class that inherits from the built-in "Exception" class or any other
appropriate base class. This new class will serve as your custom exception.

class MyCustomError(Exception):

pass

Explanation

 Inheritance − By inheriting from "Exception", your custom exception will have the same
behaviour and attributes as the built-in exceptions.
 Class Definition − The class is defined using the standard Python class syntax. For
simple custom exceptions, you can define an empty class body using the "pass" statement.

Step 2 − Initialize the Exception

Implement the "__init__" method to initialize any attributes or provide custom error
messages. This allows you to pass specific information about the error when raising the
exception.

class InvalidAgeError(Exception):

def __init__(self, age, message="Age must be between 18 and 100"):

[Link] = age

[Link] = message

super().__init__([Link])

Explanation

 Attributes − Define attributes such as "age" and "message" to store information about
the error.
 Initialization − The "__init__" method initializes these attributes. The
"super().__init__([Link])" call ensures that the base "Exception" class is properly
initialized with the error message.
 Default Message − A default message is provided, but you can override it when raising
the exception.

Step 3 − Optionally Override "__str__" or "__repr__"

31
Override the "__str__" or "__repr__" method to provide a custom string representation of
the exception. This is useful for printing or logging the exception.

class InvalidAgeError(Exception):

def __init__(self, age, message="Age must be between 18 and 100"):

[Link] = age

[Link] = message

super().__init__([Link])

def __str__(self):

return f"{[Link]}. Provided age: {[Link]}"

Explanation

 __str__ Method − The "__str__" method returns a string representation of the exception.
This is what will be displayed when the exception is printed.
 Custom Message − Customize the message to include relevant information, such as the
provided age in this example.

Raising User-Defined Exceptions


Once you have defined a custom exception, you can raise it in your code to signify
specific error conditions. Raising user-defined exceptions involves using the raise statement,
which can be done with or without custom messages and attributes.

Syntax

Following is the basic syntax for raising an exception −

raise ExceptionType(args)

Example

In this example, the "set_age" function raises an "InvalidAgeError" if the age is outside the valid
range −

32
def set_age(age):

if age < 18 or age > 100:

raise InvalidAgeError(age)

print(f"Age is set to {age}")

Handling User-Defined Exceptions

Handling user-defined exceptions in Python refers to using "try-except" blocks to catch


and respond to the specific conditions that your custom exceptions represent. This allows your
program to handle errors gracefully and continue running or to take specific actions based on the
type of exception raised.

Syntax

Following is the basic syntax for handling exceptions −

try:

# Code that may raise an exception

except ExceptionType as e:

# Code to handle the exception

Example

In the below example, the "try" block calls "set_age" with an invalid age. The "except"
block catches the "InvalidAgeError" and prints the custom error message −

try:

set_age(150)

except InvalidAgeError as e:

print(f"Invalid age: {[Link]}. {[Link]}")

33
Complete Example

Combining all the steps, here is a complete example of creating and using a user-defined
exception −

class InvalidAgeError(Exception):

def __init__(self, age, message="Age must be between 18 and 100"):

[Link] = age

[Link] = message

super().__init__([Link])

def __str__(self):

return f"{[Link]}. Provided age: {[Link]}"

def set_age(age):

if age < 18 or age > 100:

raise InvalidAgeError(age)

print(f"Age is set to {age}")

try:

set_age(150)

except InvalidAgeError as e:

print(f"Invalid age: {[Link]}. {[Link]}")

Following is the output of the above code −

Invalid age: 150. Age must be between 18 and 100

34

You might also like