Python Tutorials

Learn Python programming step by step with clear explanations, practical examples, and hands-on code. This Python tutorial covers Python fundamentals, variables, data types, operators, conditional statements, loops, functions, lists, tuples, dictionaries, sets, object-oriented programming, exception handling, modules, file handling, JSON, APIs and more. Whether you are a beginner learning Python for the first time or a developer strengthening your programming skills, use this guide as a practical reference for learning, building and revising Python concepts.

Python Introduction

Understand Python, its features, applications and basic program structure.

Python is a high-level, general-purpose programming language designed with an emphasis on readability and developer productivity. Its syntax is relatively easy to understand, which makes Python a popular choice for beginners as well as experienced developers.

Python is an interpreted language. In a typical development workflow, Python code is executed by the Python interpreter rather than being compiled into a traditional standalone executable in the same way as languages such as C or C++.

Why Is Python Popular?

Simple Syntax

Python syntax is designed to be readable, allowing developers to focus more on solving problems than on complex language syntax.

Large Ecosystem

Python has a large collection of libraries and frameworks for web development, data, automation and artificial intelligence.

Web Development

Frameworks such as Django and Flask can be used to build web applications and APIs.

Data & AI

Python is widely used for data analysis,machine learning and artificial intelligence.

Your First Python Program

The print() function is commonly used to display information in the console.

print("Hello, Python!")
Output:
Hello, Python!
Key Point: Python uses indentation to define blocks of code. This makes proper indentation an important part of writing Python programs.

Python Installation & IDLE Setup

Install Python, open IDLE, create your first Python program, and run it step by step.

Before learning Python programming, you need an environment where you can write and execute Python programs. The easiest option for beginners is IDLE, which is included with the standard Python installation.

IDLE stands for Integrated Development and Learning Environment. It provides a simple interface for writing, editing, and executing Python programs without requiring a separate code editor.

Why use IDLE? IDLE is lightweight, simple to use, and comes with Python. Beginners can start writing Python programs without installing additional development software.

What You Will Learn

Download and install Python
Add Python to the system PATH
Verify the Python installation
Open Python IDLE
Understand the IDLE Shell
Create a Python file using IDLE
Run your first Python program

Step 1 Check Whether Python Is Already Installed

Before installing Python, first check whether Python is already available on your computer.

On Windows, open Command Prompt.

  1. Press Windows + R.
  2. Type cmd.
  3. Press Enter.

Now execute:

python --version

If Python is installed, you may see output similar to:

Python 3.x.x

If Python is not installed, Windows may display a message indicating that the command cannot be found.

Step 2 Download Python

If Python is not installed, download it from the official Python website.

Choose the latest supported Python 3 release available for your operating system.

Important: Always download Python from the official Python website rather than unknown third-party websites.

Step 3 Start the Python Installation

After downloading the Python installer:

  1. Open your Downloads folder.
  2. Find the downloaded Python installer.
  3. Double-click the installer.
  4. Wait for the Python Setup window to open.
Important: Before clicking Install, look carefully at the bottom of the installation window.

Step 4 Enable "Add Python to PATH"

On the Python installation screen, you will find an option similar to:

Add python.exe to PATH

Enable this option before continuing with the installation.

PATH allows Windows to find Python when you type commands such as python in Command Prompt.

Do not skip this step. If Python is not added to PATH, you may have difficulty running Python commands from Command Prompt later.

Step 5 Install Python

After enabling the PATH option:

  1. Click Install Now.
  2. Allow the installer to copy the required Python files.
  3. Wait until the installation is completed.
  4. Click Close.
Python installation completed! IDLE is normally installed along with the standard Python installation.

Step 6 Verify the Installation

Open a new Command Prompt window and execute:

python --version

You should see a Python version:

Python 3.x.x

You can also start Python directly from the Command Prompt:

python

If Python is working correctly, you will see the Python interactive shell with the >>> prompt.


>>> print("Hello Python")
Hello Python

Exit the Python shell using:

exit()

Step 7 Open Python IDLE

IDLE is installed along with Python, so you normally do not need to install it separately.

To open IDLE in Windows:

  1. Open the Start Menu.
  2. Search for IDLE.
  3. You should see an entry similar to IDLE (Python 3.x).
  4. Click it to open IDLE.
Tip: You can also search for Python from the Start Menu and select the IDLE application from the available Python tools.

Step 8 Understand the IDLE Shell

When IDLE opens, you will normally see a window containing the Python Shell.

The Shell allows you to execute Python statements immediately without creating a Python file.

For example, type:


>>> 10 + 20
30

You can also execute:


>>> print("Welcome to Python")
Welcome to Python
What is the Shell? The Python Shell is useful for quickly testing individual Python statements, expressions, and small pieces of code.

Step 9 Create a Python Program in IDLE

The Shell is useful for testing small pieces of code, but complete programs should normally be saved in a Python file.

To create a Python file:

  1. Open IDLE.
  2. Click File.
  3. Select New File.

A new editor window will appear.

Shell vs Editor: The Shell is used for interactive execution, while the Editor is used to write and save complete Python programs.

Step 10 Write Your First Python Program

In the new IDLE editor window, type the following program:


name = "Python"

print("Welcome to", name)

This program creates a variable named name and stores the value "Python" in it.

The print() function then displays the value on the screen.

Step 11 Save the Python Program

Python programs should be saved with the .py file extension.

  1. Click File.
  2. Select Save As.
  3. Choose a folder where you want to store your programs.
  4. Enter the file name hello.py.
  5. Click Save.
Remember: Make sure the file is saved with the .py extension. Python uses this extension to identify Python source files.

Step 12 Run the Program in IDLE

After saving the program, you can execute it directly from IDLE.

  1. Make sure hello.py is open in the IDLE editor.
  2. Click Run from the menu.
  3. Select Run Module.

You can also use the keyboard shortcut:

F5

IDLE will execute the program and display the output in the Python Shell.

Welcome to Python

Step 13 Modify and Run the Program Again

One advantage of using IDLE is that you can quickly modify your program and run it again.

Change the program to:


name = "Python Programming"

print("Welcome to", name)
print("Let's start learning!")

Save the file and press F5.

The Shell will display:


Welcome to Python Programming
Let's start learning!

Step 14 Understand the Difference Between Shell and Editor

Python Shell Python Editor
Executes statements immediately Used to write complete programs
Useful for quick experiments Useful for larger programs
Usually uses the >>> prompt Contains saved .py files
Results appear immediately Program is executed using Run Module

Step 15 Run a Python File from Command Prompt

Although IDLE is convenient for beginners, it is also important to understand how Python programs are executed from a terminal.

Open Command Prompt and navigate to the folder containing hello.py.

Then run:

python hello.py

The output will be:


Welcome to Python Programming
Let's start learning!

Common Problems

IDLE is normally installed with the standard Python installation. If it is missing, check whether Python was installed correctly and whether the installation included IDLE.

You can also search the Start Menu for:

IDLE

This generally means Python is not available through PATH.

  1. Check that Python is installed.
  2. Close and reopen Command Prompt.
  3. Try python --version again.
  4. Check your Python installation and PATH configuration.

First save the Python file and make sure it has a .py extension.

Then press F5 again or select:

Run → Run Module

Python Setup Checklist

Python is installed
Python is available from Command Prompt
IDLE is available
You understand the Python Shell
You can create a .py file
You can save a Python program
You can run a program using IDLE
You can run a Python file from Command Prompt

Datatypes, Variables & Operators

Learn how Python stores data, creates variables, works with different data types, converts values, and performs operations.

Every Python program works with data. A program may need to store a person's name, calculate a salary, compare two numbers, store multiple values, or perform mathematical calculations.

Python provides variables to store or refer to values, data types to describe the kind of data being used, and operators to perform operations on that data.

In this chapter: You will learn how variables work, the major built-in Python data types, type checking, type conversion, and the different types of operators.

Variables

A variable is a name used to refer to a value stored during program execution.

For example:


name = "Arun"
age = 22
salary = 25000.50
                  

In this example:

  • name refers to the string "Arun".
  • age refers to the integer 22.
  • salary refers to the floating-point value 25000.50.

The = symbol is called the assignment operator. It assigns a value to a variable.

Important: The = operator does not mean "equal to" in the mathematical sense. It means that the value on the right is assigned to the variable on the left.
Example: Using Variables

name = "Arun"
age = 22
salary = 25000.50

print(name)
print(age)
print(salary)
                  
Output:

Arun
22
25000.5
                  
Changing the Value of a Variable

A variable can be assigned a new value at any time.


age = 22                  
print(age)                    
age = 23                        
print(age)
                  
Output:

22
23
                    

The variable age first refers to 22 and later refers to 23.

Multiple Assignment

Python allows multiple variables to be assigned in a single statement.


name, age, city = "Arun", 22, "Chennai"                    
print(name)
print(age)
print(city)
                  
Python assigns the first value to the first variable, the second value to the second variable, and so on.

Variable Naming Rules

Python has rules that must be followed when creating variable names.

Rule Example
Can contain letters studentName
Can contain numbers student1
Can use underscore student_name
Cannot start with a number 1student
Cannot contain spaces student name
Cannot use Python keywords class
Good Variable Names

student_name = "Arun"
student_age = 22
total_salary = 35000
                  
Good practice: Use meaningful variable names that describe the value they contain.

Python's Dynamic Typing

Python is a dynamically typed language. This means you do not need to explicitly specify the data type when creating a variable.

For example:


value = 10
value = "Python"
value = 25.5
                  

The same variable can refer to values of different types at different points during program execution.

Remember: Python determines the type of a value automatically. This is one of the reasons Python code can be concise and easy to write.

Python Data Types

A data type defines the kind of value that Python is working with. Different types support different operations.

Some of the most commonly used built-in Python data types are:

Data Type Example Description
int 25 Whole numbers
float 25.50 Decimal numbers
complex 3 + 4j Complex numbers
str "Python" Text or sequence of characters
bool True Boolean values
list [10, 20, 30] Ordered and mutable collection
tuple (10, 20, 30) Ordered and immutable collection
set {10, 20, 30} Collection of unique values
dict {"name": "Arun"} Key-value collection
NoneType None Represents the absence of a value

Numeric Data Types

Python provides several numeric types. The most commonly used are int, float, and complex.

Integer

The int type represents whole numbers without a decimal component.


age = 22
quantity = 100
temperature = -5
                  
Float

The float type represents numbers containing a decimal component.


price = 99.50
height = 5.8
percentage = 82.75
                  
Complex

Complex numbers contain a real part and an imaginary part. Python uses j to represent the imaginary component.


number = 3 + 4j
print(number)
                  

String Data Type

A str represents text. Strings can be created using single quotes, double quotes, or triple quotes.


name = "Arun"
city = 'Chennai'
message = """Welcome to
Python Programming"""
                  

Strings can contain letters, numbers, spaces, and special characters.


course = "Python Full Stack"
code = "PY101"
message = "Hello, Python!"
                  

Boolean Data Type

The bool data type has only two possible values: True and False.


is_logged_in = True
is_admin = False

print(is_logged_in)
print(is_admin)
                  

Boolean values are commonly used with conditions and comparison operations.

Collection Data Types

Python provides several built-in collection types for storing multiple values.

Type Example Mutable? Duplicates?
list [10, 20, 30] Yes Yes
tuple (10, 20, 30) No Yes
set {10, 20, 30} Yes No
dict {"name": "Arun"} Yes Keys must be unique
Note: Lists, tuples, sets, and dictionaries will be explored in much greater detail in the Collections section.

Checking the Data Type

Python provides the built-in type() function to determine the type of a value or variable.


age = 22
price = 99.50
name = "Arun"

print(type(age))
print(type(price))
print(type(name))
                  
Output:

                    <class 'int'>
                    <class 'float'>
                    <class 'str'>
                  

The type() function is especially useful when debugging programs or understanding what type of value a variable currently contains.

Type Conversion

Sometimes a program receives a value in one data type but needs to use it as another type. Python provides built-in functions for converting compatible values.

Function Converts To Example
int() Integer int("25")
float() Float float("25.5")
str() String str(25)
bool() Boolean bool(1)
Example: String to Integer

age = "22"              
age = int(age)                    
print(age + 5)
                  
Output:
27

Without conversion, "22" is a string rather than a number. Converting it to int allows numerical operations.

Example: Integer to String

age = 22              
message = "My age is " + str(age)              
print(message)
                  
Output:
My age is 22

Mutable and Immutable Data

Python objects can broadly be classified as mutable or immutable.

A mutable object can be changed after it is created, while an immutable object cannot be changed after creation.

Category Examples Meaning
Mutable list, dict, set Contents can be changed
Immutable int, float, str, tuple Object cannot be changed after creation

This concept becomes particularly important when working with lists, functions, and object references.

Operators

Operators are symbols or keywords used to perform operations on values and variables.

Python provides several categories of operators.

Category Operators Purpose
Arithmetic + - * / // % ** Mathematical operations
Comparison == != > < >= <= Compare values
Assignment = += -= *= /= Assign and update values
Logical and or not Combine logical conditions
Membership in not in Check membership
Identity is is not Check object identity
Bitwise & | ^ ~ << >> Perform bit-level operations

Arithmetic Operators

Arithmetic operators are used to perform mathematical calculations.

Operator Name Example Result
+ Addition 10 + 3 13
- Subtraction 10 - 3 7
* Multiplication 10 * 3 30
/ Division 10 / 3 3.333...
// Floor division 10 // 3 3
% Modulus 10 % 3 1
** Exponentiation 2 ** 3 8
Arithmetic Example

a = 10
b = 3                    
print(a + b)
print(a - b)
print(a * b)
print(a / b)
print(a // b)
print(a % b)
print(a ** b)
                  
Important:
  • / performs normal division and returns a floating-point result.
  • // performs floor division.
  • % returns the remainder.
  • ** is used for exponentiation.

Comparison Operators

Comparison operators compare two values and return either True or False.

Operator Meaning Example
== Equal to 10 == 10
!= Not equal to 10 != 5
> Greater than 10 > 5
< Less than 5 < 10
>= Greater than or equal to 10 >= 10
<= Less than or equal to 5 <= 10

a = 10
b = 3                    
print(a > b)
print(a == b)
print(a != b)
                  
Output:

True
False
True
                  

Assignment Operators

Assignment operators are used to assign values to variables and update their existing values.

Operator Example Equivalent To
= x = 10 Assign 10 to x
+= x += 5 x = x + 5
-= x -= 5 x = x - 5
*= x *= 5 x = x * 5
/= x /= 5 x = x / 5

score = 100              
score += 20                    
print(score)
                  
Output:
120

Logical Operators

Logical operators are used to combine or modify Boolean expressions.

Operator Description
and Returns a truthy result only when both conditions are true.
or Returns a truthy result when at least one condition is true.
not Reverses the Boolean truth value.

age = 22
has_id = True                    
print(age >= 18 and has_id)
print(age < 18 or has_id)
print(not has_id)
                  

Membership Operators

Membership operators check whether a value exists inside a collection such as a string, list, tuple, or set.

Python provides:

  • in
  • not in

languages = ["Python", "Java", "C++"]              
print("Python" in languages)
print("PHP" not in languages)
                  
Output:

True
True
                  

Operator Precedence

When an expression contains multiple operators, Python follows a specific order to determine which operation should be performed first.

For example:


result = 10 + 5 * 2              
print(result)
                  
Output:
20

Multiplication is performed before addition, so the expression is evaluated as:


10 + (5 * 2)
10 + 10
20
                  

Parentheses can be used when you want to explicitly control the order of evaluation.


result = (10 + 5) * 2                    
print(result)
                  
Output:
30

Common Beginner Mistakes

= assigns a value, while == compares two values.


age = 22                            
print(age == 22)
                          

A value such as "100" is a string, not an integer.


age = "22"                            
age = int(age)                            
print(age + 5)
                          

Convert the value to the required data type before performing numerical calculations.

For example, this is invalid:

2name = "Arun"

Variable names cannot begin with a number.

Use:

name2 = "Arun"

Conditional Statements & Loops

Control the flow of your Python programs using decisions and repetition.

A Python program normally executes statements from top to bottom. However, real-world programs often need to make decisions or repeat certain operations multiple times.

For example, a program may need to check whether a student has passed, determine whether a person is eligible to vote, display different messages based on a user's age, or process every item in a list.

Python provides conditional statements for decision-making and loops for repeated execution.

In this chapter: You will learn if, elif, else, nested conditions, for loops, while loops, range(), nested loops, break, continue, and pass.

Conditional Statements

Conditional statements allow a program to make decisions based on whether a condition is True or False.

Python commonly uses:

  • if
  • if ... else
  • if ... elif ... else
  • Nested if statements

Conditions usually contain comparison or logical operators.

The if Statement

The if statement executes a block of code only when its condition evaluates to True.

Basic syntax:


if condition:
  statement

For example:


age = 20

if age >= 18:
    print("You are eligible to vote")
Output:
You are eligible to vote

Since age >= 18 evaluates to True, Python executes the indented print() statement.

Indentation in Python

Python uses indentation to define a block of code. Unlike languages that use curly braces such as { }, Python uses whitespace to determine which statements belong to a conditional or loop.


age = 20

if age >= 18:
    print("Eligible")
    print("Age requirement satisfied")

Both print() statements belong to the if block because they are indented.

Important: Consistent indentation is required in Python. A common convention is to use four spaces for each level of indentation.

The if...else Statement

The else block is executed when the if condition is False.


age = 16

if age >= 18:
    print("You are eligible to vote")
else:
    print("You are not eligible to vote")
Output:
You are not eligible to vote

Only one of the two blocks is executed.

The if...elif...else Statement

When there are multiple possible conditions, Python provides elif, which means "else if".


marks = 75

if marks >= 90:
    grade = "A"
elif marks >= 60:
    grade = "B"
else:
    grade = "C"

print(grade)
Output:
B

Python checks the conditions from top to bottom. Once it finds a condition that is True, its corresponding block executes and the remaining conditions are skipped.

Using Multiple elif Conditions

You can use multiple elif blocks when a program needs to handle several possible outcomes.


marks = 82

if marks >= 90:
    grade = "A+"
elif marks >= 80:
    grade = "A"
elif marks >= 70:
    grade = "B"
elif marks >= 60:
    grade = "C"
else:
    grade = "F"

print(grade)
Output:
A

Using Comparison Operators in Conditions

Conditions commonly use comparison operators such as:

Operator Meaning Example
== Equal to age == 18
!= Not equal to age != 18
> Greater than marks > 50
< Less than marks < 50
>= Greater than or equal to age >= 18
<= Less than or equal to age <= 18

Using Logical Operators in Conditions

Logical operators allow multiple conditions to be combined.


age = 25
has_id = True

if age >= 18 and has_id:
    print("Entry allowed")
else:
    print("Entry denied")
Output:
Entry allowed

Here, both conditions must be true because the and operator is being used.

Nested if Statements

An if statement can be placed inside another if statement. This is called a nested if.


age = 22
has_id = True

if age >= 18:
    if has_id:
        print("Entry allowed")
    else:
        print("ID is required")
else:
    print("You are underage")

The inner condition is checked only when the outer condition is true.

Tip: Nested conditions are useful, but too many levels of nesting can make code difficult to read. Logical operators or well-designed functions can often make complex conditions easier to understand.

Practical Example: Login Validation

Conditional statements are frequently used when validating information.


username = "admin"
password = "python123"

if username == "admin" and password == "python123":
    print("Login successful")
else:
    print("Invalid username or password")
Output:
Login successful

Loops

A loop allows a block of code to execute repeatedly.

Instead of writing the same statement many times, you can use a loop to perform the operation automatically.

Python provides two main types of loops:

  • for loop
  • while loop

The for Loop

A for loop is used to iterate over the items of an iterable such as a list, tuple, string, set, or range.

For example:


for number in range(1, 6):
  print(number)
Output:

1
2
3
4
5

During each iteration, the variable number receives the next value produced by range(1, 6).

Understanding range()

The range() function is commonly used with for loops to generate a sequence of numbers.

When using two arguments, range(start, stop) begins at start and stops before stop.


for number in range(1, 6):
  print(number)

The values are:


1
2
3
4
5
Remember: The stop value is not included. Therefore, range(1, 6) produces numbers from 1 through 5.

Using a Step with range()

A third argument can be used to specify how much the value should change during each iteration.


for number in range(2, 11, 2):
  print(number)
Output:

2
4
6
8
10

Using for with a String

A string is iterable, so a for loop can process one character at a time.


name = "Python"

for character in name:
    print(character)
Output:

P
y
t
h
o
n

Using for with a List

A for loop can also process each item in a list.


languages = ["Python", "Java", "C++"]

for language in languages:
    print(language)
Output:

Python
Java
C++

Nested for Loops

A loop can be placed inside another loop. This is called a nested loop.


for row in range(1, 4):
  for column in range(1, 4):
      print(row, column)

The inner loop completes all of its iterations for every iteration of the outer loop.

Use carefully: Nested loops are useful for tables, patterns, and multidimensional data, but too many nested loops can make programs harder to understand and may increase execution time.

The while Loop

A while loop repeatedly executes a block of code as long as its condition remains True.

Example:


count = 1

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

1
2
3
4
5

The variable count changes during every iteration. Once the condition count <= 5 becomes false, the loop stops.

Avoiding Infinite Loops

A while loop must eventually reach a condition that becomes false. Otherwise, it can continue running indefinitely.

For example:


count = 1

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

Here, count += 1 changes the value used by the condition. Without an appropriate update, the condition might never become false.

Common mistake: Forgetting to update the variable controlling a while loop can create an infinite loop.

for Loop vs while Loop

for Loop while Loop
Commonly used to iterate over an iterable. Runs while a condition remains true.
Useful when processing a known sequence. Useful when repetition depends on a condition.
Often used with range(). Requires a condition that eventually becomes false.
Loop progression is usually handled by the iterator. The programmer often needs to update a control variable.

The break Statement

The break statement immediately terminates the current loop. Python continues execution with the statement after the loop.


for number in range(1, 10):

  if number == 5:
      break

  print(number)
Output:

1
2
3
4

When number becomes 5, break stops the loop.

The continue Statement

The continue statement skips the remaining statements in the current iteration and moves to the next iteration.


for number in range(1, 6):

  if number == 3:
      continue

  print(number)
Output:

1
2
4
5

When number becomes 3, Python skips the print() statement for that iteration and continues with the next value.

break vs continue

Statement What it does
break Completely stops the current loop.
continue Skips the current iteration and continues the loop.

The pass Statement

The pass statement does nothing. It is used as a placeholder when Python requires a statement but you do not want to execute any code yet.


age = 20

if age >= 18:
    pass
else:
    print("Underage")

pass is useful while developing a program when a block is planned but its implementation has not been written yet.

else with Loops

Python also allows an else block to be associated with a loop. The else block executes when the loop finishes normally without encountering break.


for number in range(1, 4):
    print(number)
else:
    print("Loop completed")
Output:

1
2
3
Loop completed
Important: If the loop is terminated using break, the loop's else block does not execute.

Practical Example: Student Result

Conditional statements and loops are often combined to solve practical problems.

The following example checks the result of multiple students:


marks = [85, 72, 45, 91, 38]

for mark in marks:

    if mark >= 50:
        print(mark, "Pass")
    else:
        print(mark, "Fail")
Output:

85 Pass
72 Pass
45 Fail
91 Pass
38 Fail

Here, the for loop processes every mark, while the if...else statement determines whether each student has passed or failed.

Practical Example: Finding Even Numbers

The modulus operator can be combined with a loop and a condition to identify even numbers.


for number in range(1, 11):

  if number % 2 == 0:
      print(number)
Output:

2
4
6
8
10

Common Beginner Mistakes

Python requires a colon after conditions and loop statements.


if age >= 18:
  print("Eligible")

Statements belonging to a block must have consistent indentation.


if age >= 18:
  print("Eligible")
  print("You can continue")

Make sure the condition of a while loop can eventually become false.


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

Updating count allows the condition to eventually become false.

  • break stops the entire loop.
  • continue skips only the current iteration.

Python Functions

Create reusable, organized, and maintainable blocks of code.

A function is a reusable block of code designed to perform a specific task. Instead of writing the same logic repeatedly, you can place it inside a function and call that function whenever you need it.

Functions are one of the most important concepts in Python because they help break a large program into smaller and more manageable parts.

For example, an application may contain separate functions for calculating salary, validating a user, calculating an order total, sending a message, or processing data.

Why use functions?
  • Reduce code repetition
  • Divide a large program into smaller tasks
  • Improve code readability
  • Make code easier to test and debug
  • Allow the same logic to be reused multiple times
  • Make programs easier to maintain

Understanding a Function

A Python function is created using the def keyword. The function has a name, optional parameters, and a block of statements.


def function_name(parameters):
  statements
  return value
Part Purpose
def Keyword used to define a function.
Function name Name used to identify and call the function.
Parameters Input values accepted by the function.
: Marks the beginning of the function block.
Function body Contains the statements that perform the task.
return Optionally sends a value back to the caller.

Creating a Function

The following function displays a welcome message:


def greet():
  print("Welcome to Python")

greet()
Output:
Welcome to Python

The function is defined using def greet():. The statement inside the function is executed only when greet() is called.

Defining and Calling a Function

There is an important difference between defining a function and calling a function.

Defining

Defining a function means creating the function and specifying what it should do.


def greet():
print("Hello")
Calling

Calling a function means executing the function.

greet()

Parameters and Arguments

Functions can accept values from the code that calls them. These values are commonly referred to as arguments.

A parameter is the variable defined inside the function definition, while an argument is the actual value passed when the function is called.


def greet(name):
  print("Hello", name)

greet("Arun")
greet("Priya")
Output:

Hello Arun
Hello Priya

Here, name is the parameter, while "Arun" and "Priya" are arguments.

Multiple Parameters

A function can accept multiple parameters. Each parameter can represent a different piece of information required by the function.


def student(name, age, course):
  print("Name:", name)
  print("Age:", age)
  print("Course:", course)

student("Arun", 22, "Python")
Output:

Name: Arun
Age: 22
Course: Python

Returning a Value

A function can perform a calculation and send the result back to the part of the program that called it. The return statement is used for this purpose.


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

result = add(10, 20)

print(result)
Output:
30

The value returned by add() is stored in the result variable.

print() vs return

Beginners often confuse print() and return. They serve different purposes.

print() return
Displays a value on the screen. Sends a value back to the caller.
Mainly used for displaying information. Used when another part of the program needs the result.
Does not normally provide the displayed value to another operation. The returned value can be stored, calculated, or passed elsewhere.

For reusable logic, return is often more useful than simply printing the result.

Returning Multiple Values

Python allows a function to return multiple values. Internally, Python packages the returned values together, and they can be assigned to multiple variables.


def calculate(a, b):
  total = a + b
  difference = a - b
  return total, difference

result, difference = calculate(20, 5)

print(result)
print(difference)
Output:

25
15

Default Arguments

A parameter can have a default value. If the caller does not provide a value for that parameter, Python uses the default value.


def greet(name="Student"):
  print("Hello", name)

greet()
greet("Arun")
Output:

Hello Student
Hello Arun

In the first call, no value is supplied, so Python uses "Student".

Keyword Arguments

Keyword arguments allow you to pass values by explicitly specifying the parameter name.


def student(name, age):
  print("Name:", name)
  print("Age:", age)

student(age=22, name="Arun")
Output:

Name: Arun
Age: 22

Notice that the arguments are supplied in a different order from the function definition. Because the parameter names are specified, Python knows which value belongs to which parameter.

Positional Arguments

Positional arguments are assigned to parameters according to their position.


def student(name, age):
  print(name)
  print(age)

student("Arun", 22)

The first argument is assigned to name and the second argument is assigned to age.

Positional vs Keyword Arguments

Type Example How values are assigned
Positional student("Arun", 22) Based on position.
Keyword student(age=22, name="Arun") Based on parameter name.

Variable Scope in Functions

The scope of a variable determines where that variable can be accessed. Variables created inside a function are generally local to that function.


def calculate():
  result = 100
  print(result)

calculate()

The variable result belongs to the function's local scope. It is available inside the function where it was created.

Local and Global Variables

A variable created outside a function is generally considered a global variable, while a variable created inside a function is local to that function.


course = "Python"

def display():
  name = "Arun"

  print(course)
  print(name)

display()

The function can access the global course variable and its own local name variable.

Calling One Function from Another

Functions can call other functions. This makes it possible to divide a larger task into smaller reusable operations.


def calculate_total(price, quantity):
  return price * quantity


def display_bill():
  total = calculate_total(500, 3)
  print("Total:", total)


display_bill()
Output:
Total: 1500

Here, display_bill() calls calculate_total() to perform the calculation.

Common Beginner Mistakes

Defining a function does not automatically execute it. You must call it when you want its code to run.


def greet():
  print("Hello")

greet()

If another part of the program needs the result of a calculation, the function should normally return that value.


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

The number of required positional arguments should normally match the parameters defined by the function.


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

add(10, 20)

Parameters are defined in the function declaration, while arguments are the actual values supplied during the function call.

Function Best Practices

Keep Functions Focused

A function should ideally perform one clear responsibility rather than trying to handle many unrelated tasks.

Use Meaningful Names

Names such as calculate_total() and validate_user() make the purpose of a function clear.

Avoid Unnecessary Repetition

If the same logic appears in multiple places, consider moving it into a reusable function.

Return Results When Appropriate

Returning values allows the calling code to decide how the result should be displayed or processed.

Modules & Packages

Organize Python programs into reusable components.

As applications become larger, keeping every function and class in a single file becomes difficult. Python modules and packages provide a way to organize code into reusable components.

Using a Built-in Module


import math

print(math.sqrt(25))
print(math.pi)

Importing Specific Functions


from math import sqrt

print(sqrt(36))

Module Alias


import math as m

print(m.sqrt(49))

Custom Modules

Suppose we create a file named calculator.py:


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

Another Python file can import and use that function:


import calculator

print(calculator.add(10, 20))
Modules are useful for separating related functionality, while packages help organize multiple modules into a larger project structure.

Python Data Structures

Store, organize and manipulate collections of data efficiently.

A data structure determines how multiple values are stored, organized, accessed and modified inside a program. Choosing the correct data structure can make a program easier to understand and more efficient.

Python provides several built-in data structures. The four most commonly used collection types are list, tuple, set and dictionary.

Important: Lists, tuples, sets and dictionaries are not interchangeable. Each one has different characteristics and is suitable for different types of problems.

Python's Built-in Collection Types

Data Structure Ordered Mutable Allows Duplicates Access Method
list Yes Yes Yes Index
tuple Yes No Yes Index
set No* Yes No Membership
dict Yes** Yes Keys must be unique Key

* Sets are unordered collections and should not be used when positional order is required.
** Dictionaries preserve insertion order in modern Python versions.

Lists

A list is an ordered and mutable collection. It can contain multiple values and those values can be changed after the list is created.

Lists can contain values of the same type or different types.


students = ["Arun", "Priya", "Kumar"]

print(students)
Output:
['Arun', 'Priya', 'Kumar']

List Indexing

Every element in a list has a position called an index. Python uses zero-based indexing, so the first element has index 0.

Value Arun Priya Kumar Meena
Positive Index 0 1 2 3
Negative Index -4 -3 -2 -1

students = ["Arun", "Priya", "Kumar", "Meena"]

print(students[0])
print(students[2])
print(students[-1])
Output:

Arun
Kumar
Meena

Modifying List Elements

Lists are mutable, so individual elements can be changed using their index.


students = ["Arun", "Priya", "Kumar"]

students[1] = "Meena"

print(students)
Output:
['Arun', 'Meena', 'Kumar']

Common List Methods

Method Purpose Example
append() Adds an element to the end. items.append(50)
insert() Inserts an element at a specific position. items.insert(1, 20)
extend() Adds multiple elements. items.extend([60, 70])
remove() Removes the first matching value. items.remove(20)
pop() Removes and returns an element. items.pop()
sort() Sorts the list. items.sort()
reverse() Reverses the list. items.reverse()
count() Counts occurrences of a value. items.count(20)
index() Returns the position of a value. items.index(20)
clear() Removes all elements. items.clear()

Tuples

A tuple is an ordered and immutable collection. Like lists, tuples support indexing and slicing, but their elements cannot normally be changed after creation.


coordinates = (10, 20, 30)

print(coordinates[0])
print(coordinates[-1])
Output:

10
30

Tuple Immutability

Because tuples are immutable, an existing element cannot simply be replaced.


coordinates = (10, 20, 30)

# coordinates[0] = 50
Remember: Use a tuple when the collection should represent fixed data that should not be modified accidentally.

Tuple Methods

Since tuples cannot be modified, they have fewer methods than lists. Two commonly used methods are count() and index().


numbers = (10, 20, 20, 30)

print(numbers.count(20))
print(numbers.index(30))
Output:

2
3

Sets

A set is a mutable collection that stores unique values. Duplicate elements are automatically removed.


numbers = {10, 20, 20, 30, 30}

print(numbers)
Output:
{10, 20, 30}

Adding Elements to a Set


numbers = {10, 20, 30}

numbers.add(40)

print(numbers)

Removing Elements from a Set

Sets provide methods such as remove() and discard().


numbers = {10, 20, 30}

numbers.remove(20)

print(numbers)
Difference: remove() raises an error if the element does not exist, while discard() does not.

Dictionaries

A dictionary stores data as key-value pairs. Each key identifies a corresponding value.


student = {
    "name": "Arun",
    "age": 22,
    "course": "Python"
}

print(student["name"])
print(student["course"])
Output:
Arun
              Python

Dictionary Keys and Values

A dictionary consists of keys and values. Keys must be unique, while values can be duplicated.


student = {
    "name": "Arun",
    "age": 22,
    "course": "Python"
}

print(student.keys())
print(student.values())
print(student.items())

Adding and Updating Dictionary Data

Dictionaries are mutable. A new key-value pair can be added, and an existing value can be updated using its key.


student = {
    "name": "Arun",
    "age": 22
}

student["course"] = "Python"
student["age"] = 23

print(student)
Output:
{'name': 'Arun', 'age': 23, 'course': 'Python'}

Common Dictionary Methods

Method Purpose
get() Returns the value associated with a key.
keys() Returns dictionary keys.
values() Returns dictionary values.
items() Returns key-value pairs.
update() Adds or updates multiple key-value pairs.
pop() Removes a key and returns its value.
popitem() Removes and returns the last inserted key-value pair.
clear() Removes all key-value pairs.

Nested Data Structures

Python data structures can contain other data structures. This is useful when representing real-world data.


students = [
    {
        "name": "Arun",
        "age": 22,
        "course": "Python"
    },
    {
        "name": "Priya",
        "age": 21,
        "course": "Java"
    }
]

print(students[0]["name"])
print(students[1]["course"])
Output:

              Arun
              Java

This type of structure is commonly encountered when working with JSON data, APIs and database records.

List of Lists

A list can also contain other lists. This is useful for representing table-like or matrix-style data.


marks = [
    [80, 75, 90],
    [70, 85, 88],
    [90, 92, 95]
]

print(marks[0][1])
Output:
75

Mutable vs Immutable

One of the most important concepts when working with Python data structures is mutability.

A mutable object can be changed after it is created. An immutable object cannot normally be changed after creation.

Data Type Mutable?
List Yes
Tuple No
Set Yes
Dictionary Yes
String No

Which Data Structure Should You Use?

Requirement Recommended Structure Reason
Ordered collection that changes list Ordered and mutable.
Fixed collection of values tuple Ordered and immutable.
Unique values set Automatically eliminates duplicates.
Key-value information dict Values can be accessed using meaningful keys.
Student records list + dict Multiple records can be stored as dictionaries inside a list.
Unique user roles set Duplicate roles are automatically avoided.
Coordinates tuple Coordinates normally represent fixed values.

Common Mistakes

Use a list when the data needs to change. Use a tuple when the collection represents fixed data.

Sets are designed for unique values and membership operations, not positional access.

Dictionary keys must be unique. If the same key is assigned again, its previous value is replaced.

Modifying a collection while looping through it can produce unexpected behavior. In many situations, creating a separate collection or using a comprehension is safer.

OOP Concepts: Classes, Objects, Inheritance & Polymorphism

Understand how Python uses objects to organize data and behavior.

Object-Oriented Programming, commonly called OOP, is a programming approach where data and the operations performed on that data are organized into objects.

Instead of writing an entire program as a collection of unrelated functions, OOP allows us to model real-world entities such as students, employees, customers, products and vehicles as objects.

Python supports several important OOP concepts including classes, objects, attributes, methods, constructors, inheritance and polymorphism.

Simple idea: A class is a blueprint, while an object is an actual instance created from that blueprint.

Classes and Objects

A class defines the properties and behaviors that its objects will have. It acts like a blueprint for creating objects.

An object is an actual instance of a class. Multiple objects can be created from the same class, and each object can contain different data.

Example:

  • Class: Student
  • Objects: Arun, Priya, Kumar
  • Attributes: name, age, course
  • Methods: study(), attend_class()

class Student:

  def greet(self):
      print("Hello Student")


student1 = Student()

student1.greet()
Output:
Hello Student

Here, Student is the class and student1 is an object created from that class. The greet() method defines a behavior that the object can perform.

Understanding self

The self parameter refers to the current object. It allows methods inside a class to access the attributes and other methods belonging to that particular object.


class Student:

  def greet(self):
      print("Hello", self)


student1 = Student()
student2 = Student()

student1.greet()
student2.greet()

When student1.greet() is called, self refers to student1. When student2.greet() is called, self refers to student2.

Remember: self is not a separate object. It is a reference to the current object on which the method is being called.

Attributes and Methods

An object generally contains two important types of information: attributes and methods.

Term Meaning Example
Attribute Data or property belonging to an object. student.name
Method Function defined inside a class. student.greet()

Constructor: __init__()

The __init__() method is commonly used to initialize the attributes of an object when it is created.

It is automatically called when a new object is created from the class. This allows each object to start with its own initial data.


class Student:

  def __init__(self, name, age):
      self.name = name
      self.age = age


student = Student("Arun", 22)

print(student.name)
print(student.age)
Output:
Arun
              22

In this example, name and age are attributes of the object. The values "Arun" and 22 are passed when the object is created.

Creating Multiple Objects

A single class can be used to create multiple objects. Each object can contain different attribute values.


class Student:

  def __init__(self, name, age):
      self.name = name
      self.age = age


student1 = Student("Arun", 22)
student2 = Student("Priya", 21)

print(student1.name)
print(student2.name)
Output:

              Arun
              Priya

Both objects belong to the same Student class, but they contain different data.

Inheritance

Inheritance allows one class to acquire properties and methods from another class.

The existing class is called the parent class or base class. The class that inherits from it is called the child class or derived class.

Inheritance is useful when different classes share common functionality. Instead of writing the same code repeatedly, the common functionality can be placed in a parent class.


class Animal:

  def speak(self):
      print("Animal makes a sound")


class Dog(Animal):

  def bark(self):
      print("Dog barks")


dog = Dog()

dog.speak()
dog.bark()
Output:

              Animal makes a sound
              Dog barks

Dog inherits from Animal. Therefore, the Dog object can use the speak() method inherited from Animal, in addition to its own bark() method.

Common Types of Inheritance

Python supports different inheritance relationships depending on how classes are connected.

Type Description
Single Inheritance One child class inherits from one parent class.
Multilevel Inheritance A class inherits from a class that already inherits from another class.
Multiple Inheritance A child class inherits from more than one parent class.
Hierarchical Inheritance Multiple child classes inherit from the same parent class.

Method Overriding

Method overriding occurs when a child class provides its own implementation of a method that already exists in the parent class.

This allows the child class to change or specialize the behavior inherited from the parent.


class Animal:

  def sound(self):
      print("Animal sound")


class Dog(Animal):

  def sound(self):
      print("Bark")


dog = Dog()

dog.sound()
Output:
Bark

Although Animal contains a sound() method, Dog provides its own version. Therefore, when dog.sound() is called, the implementation in Dog is executed.

Using super()

The super() function can be used to access methods or functionality from the parent class.


class Animal:

  def sound(self):
      print("Animal sound")


class Dog(Animal):

  def sound(self):
      super().sound()
      print("Bark")


dog = Dog()

dog.sound()
Output:

              Animal sound
              Bark

Here, super().sound() calls the parent class implementation before the child class adds its own behavior.

Polymorphism

Polymorphism means "many forms". In OOP, it allows the same method or operation to produce different behavior depending on the object that is using it.

This is useful when different classes provide the same method name but implement that method differently.


class Dog:

  def sound(self):
      print("Bark")


class Cat:

  def sound(self):
      print("Meow")


animals = [Dog(), Cat()]

for animal in animals:
  animal.sound()
Output:

Bark
Meow

The loop does not need to know whether the object is a Dog or a Cat. It simply calls sound(). Python automatically uses the implementation belonging to the current object.

Key idea: Polymorphism allows different objects to respond to the same method call in their own way.

Encapsulation

Encapsulation means keeping data and the methods that operate on that data together inside a class. It also helps control how an object's internal data is accessed or modified.

Python uses naming conventions such as a single underscore (_name) and double underscore (__name) to indicate protected or private-like attributes.


class BankAccount:

  def __init__(self, balance):
      self.__balance = balance

  def show_balance(self):
      print(self.__balance)


account = BankAccount(5000)

account.show_balance()
Output:
5000

The double underscore makes __balance name-mangled by Python, which helps prevent direct accidental access from outside the class.

Abstraction

Abstraction means exposing only the necessary details while hiding unnecessary implementation details.

For example, when using a car, a driver uses the steering wheel, accelerator and brakes without needing to understand every internal operation of the engine.

Python provides the abc module for creating abstract classes and abstract methods.


from abc import ABC, abstractmethod              

class Animal(ABC):

    @abstractmethod
    def sound(self):
        pass


class Dog(Animal):

    def sound(self):
        print("Bark")


dog = Dog()

dog.sound()
Output:
Bark

Four Major Principles of OOP

Principle Purpose Python Example
Encapsulation Organizes and controls access to data and behavior. Classes and private-like attributes
Inheritance Allows classes to reuse functionality from other classes. class Dog(Animal)
Polymorphism Allows the same interface to have different implementations. animal.sound()
Abstraction Hides implementation details and exposes essential behavior. Abstract classes

Exception Handling in Python

Handle runtime errors safely and prevent applications from stopping unexpectedly.

An exception is an event that occurs while a program is running and interrupts the normal flow of execution. Exceptions commonly occur when a program receives invalid input, performs an invalid operation, or tries to access something that does not exist.

For example, dividing a number by zero, converting invalid text into an integer, or opening a file that does not exist can cause exceptions.

Python provides try, except, else, finally and raise to handle and control exceptions.

Simple idea: Exception handling allows your program to respond to an error instead of abruptly terminating the application.

Errors and Exceptions

During Python programming, you may encounter different types of problems. Some problems occur before the program can execute, while others occur during execution.

Type When It Occurs Example
Syntax Error When Python syntax is written incorrectly. Missing colon after an if statement
Exception During program execution. Dividing a number by zero

Exception handling mainly deals with problems that occur while the program is running.

Basic try-except

The try block contains code that may produce an exception. If an exception occurs, Python stops executing the remaining statements inside the try block and looks for a matching except block.


try:
  number = int(input("Enter a number: "))

  print(10 / number)

except ZeroDivisionError:
  print("Cannot divide by zero")
Example Output:

  Enter a number: 0
Cannot divide by zero

If the user enters 0, Python raises a ZeroDivisionError. The except block catches that exception and displays a meaningful message instead of allowing the program to terminate with an unhandled exception.

How try-except Works

1. try: Python executes the statements inside the try block.
2. Exception occurs: If an exception occurs, Python stops the remaining statements in the try block.
3. except: Python searches for an except block that matches the exception.
4. Continue: After the exception is handled, the program can continue executing.

Common Python Exceptions

Python provides many built-in exception types. Each exception represents a particular type of runtime problem.

Exception Meaning Example Situation
ValueError Value has an inappropriate format. Converting "abc" to an integer
TypeError Operation is performed on an incompatible type. Adding a string and an integer
ZeroDivisionError Division or modulo operation uses zero. 10 / 0
IndexError Sequence index does not exist. Accessing an unavailable list index
KeyError Dictionary key does not exist. Accessing a missing dictionary key
FileNotFoundError Requested file cannot be found. Opening a file that does not exist

Handling Multiple Exceptions

A single block of code may produce different types of exceptions. Python allows multiple except blocks so that each exception can be handled appropriately.


try:
  number = int(input("Enter a number: "))

  result = 10 / number

  print(result)

except ValueError:
  print("Please enter a valid number")

except ZeroDivisionError:
  print("Number cannot be zero")
Example:
  • If the user enters abc, a ValueError occurs.
  • If the user enters 0, a ZeroDivisionError occurs.
  • If the user enters 5, the calculation executes normally.

Using separate except blocks makes it possible to provide a different response for each type of problem.

Accessing the Exception Message

The as keyword can be used to store the exception object in a variable. This allows the program to inspect or display information about the error.


try:
  number = int("abc")

except ValueError as error:
  print("Error:", error)
Output:
Error: invalid literal for int() with base 10: 'abc'

This technique is useful when you need more information about the exception while debugging or logging an application.

else and finally

Python also provides else and finally blocks to give more control over exception handling.

  • else executes only when no exception occurs.
  • finally executes whether an exception occurs or not.

try:
  number = int(input("Enter number: "))

except ValueError:
  print("Invalid input")

else:
  print("You entered:", number)

finally:
  print("Program completed")
Example Output:

Enter number: 25
You entered: 25
Program completed

Understanding the Execution Flow

Situation try except else finally
No exception Executed Skipped Executed Executed
Exception occurs Stops at exception Executed if matched Skipped Executed
Remember: The finally block is commonly used for cleanup operations that should happen regardless of whether the operation succeeded or failed.

Why Use finally?

The finally block is useful when some operation must be performed regardless of the result. For example, a program may need to close a file, release a resource, or disconnect from a service.


try:
  print("Processing data")

except Exception:
  print("Something went wrong")

finally:
  print("Cleanup completed")

Raising an Exception

Normally, Python raises exceptions when it detects a problem during execution. However, sometimes the programmer needs to deliberately generate an exception when a specific condition is not acceptable.

The raise statement is used to explicitly raise an exception.


age = -5

if age < 0:
    raise ValueError("Age cannot be negative")
Output:
ValueError: Age cannot be negative

This is useful when a program has a business rule or validation rule. For example, an application may reject a negative age, an invalid salary, or an empty username.

Using raise with try-except

An exception raised using raise can also be handled using try-except.


try:
  age = -5

  if age < 0:
      raise ValueError("Age cannot be negative")

except ValueError as error:
  print(error)
Output:
Age cannot be negative

Custom Exceptions

Python also allows developers to create their own exception classes. Custom exceptions are useful when an application contains specific business rules that are not clearly represented by Python's built-in exceptions.


class InvalidAgeError(Exception):
    pass


age = -2

if age < 0:
    raise InvalidAgeError("Age cannot be negative")

Here, InvalidAgeError is a custom exception created by inheriting from Python's built-in Exception class.

Catching a General Exception

A general except Exception can catch many common runtime exceptions. However, specific exceptions are usually preferred because they make the program easier to understand and debug.


try:
    result = 10 / 0

except Exception as error:
    print("An error occurred:", error)
Best Practice: Catch the specific exception you expect whenever possible instead of using a broad exception handler for everything.

Exception Handling Best Practices

Handle specific exceptions: Catch ValueError, TypeError, FileNotFoundError, etc., when you know what can occur.
Do not hide errors: Avoid empty except blocks because they can make debugging difficult.
Use meaningful messages: Tell the user what went wrong and, when appropriate, how to correct it.
Use finally for cleanup: Use finally when an operation must happen regardless of success or failure.
Use raise for validation: Use raise when your application needs to reject invalid data or business conditions.

File Handling in Python

Read, write, update and manage files using Python.

File handling is used when a Python program needs to store or retrieve information from files. Unlike variables, which hold data temporarily while a program is running, files allow information to be stored permanently on a storage device.

For example, a program can use files to store student records, employee information, application logs, configuration data, reports and other information.

Python provides the built-in open() function for opening files. After opening a file, we can read from it, write to it, append data to it, and finally close it.

Simple idea: File handling allows a Python program to communicate with data stored outside the program.

Basic File Handling Process

Working with a file generally involves a few important steps:

1. Open: Open the file using the open() function.
2. Perform an operation: Read, write or append data.
3. Close: Close the file after completing the operation.
Important: A file should be properly closed after use so that system resources are released and data is safely written.

Opening a File

Python uses the open() function to open a file. The function returns a file object that can be used to perform operations on the file.

file = open("message.txt", "r")

Here, message.txt is the file name and "r" specifies that the file should be opened in read mode.

File Modes

The second argument of open() determines what operation Python should perform on the file.

Mode Purpose Important Behavior
r Read File must already exist.
w Write Creates a file or replaces existing content.
a Append Adds new content to the end of the file.
x Create Creates a new file and fails if the file already exists.
r+ Read and write Allows both reading and writing.
b Binary mode Used for binary data such as images or other binary files.
Be careful with w mode: Opening an existing file in write mode removes its previous content before writing new data.

Writing to a File

The write() method is used to store text inside a file. When a file is opened using w mode, Python creates the file if it does not already exist.


file = open("message.txt", "w")

file.write("Welcome to Python")

file.close()

After executing this program, a file named message.txt will contain:

Welcome to Python

Writing Multiple Lines

Multiple lines can be written by including newline characters (\n) between the lines.


file = open("students.txt", "w")

file.write("Arun\n")
file.write("Priya\n")
file.write("Kumar\n")

file.close()

The \n character moves the next text to a new line.


Arun
Priya
Kumar

Reading a File

The read() method reads the contents of a file. The file must normally be opened in r mode for reading.


file = open("message.txt", "r")

content = file.read()

print(content)

file.close()
Output:
Welcome to Python

Different Ways to Read a File

Python provides several methods for reading file contents. The method you choose depends on how much data you need to process at a time.

Method Purpose
read() Reads the entire file or a specified number of characters.
readline() Reads one line at a time.
readlines() Reads all lines and returns them as a list.

Appending Data to a File

The a mode is used when you want to add new content without removing the existing content.


file = open("students.txt", "a")

file.write("Meena\n")

file.close()

If the file already contains:


Arun
Priya
Kumar

After appending Meena, the file becomes:


Arun
Priya
Kumar
Meena
                  
Key difference: w replaces existing content, while a preserves existing content and adds new data at the end.

File Paths

Python can work with files located in the same folder as the Python program or in another directory.

When only the file name is provided, Python looks for the file relative to the program's current working directory.

with open("data.txt", "r") as file:
                  content = file.read()

You can also specify a path to a file located inside another folder.

with open("data/students.txt", "r") as file:
                  content = file.read()
Tip: When working with Windows paths, using a raw string such as r"C:\Users\Student\data.txt" can help avoid problems caused by backslashes being interpreted as escape characters.

Text Files and Binary Files

Python can work with both text files and binary files.

Type Examples Mode
Text .txt, .csv, .json r, w, a
Binary Images, PDFs and other binary data rb, wb

Binary mode is useful when the file contains data that should not be interpreted as ordinary text.

File Handling Best Practices

Prefer with open(): It automatically closes the file after the operation.
Choose the correct mode: Use r for reading, w for replacing content and a for adding content.
Be careful with w: It can overwrite existing file contents.
Handle file errors: Use exception handling for situations such as missing files or permission problems.
Use encoding: Specify encoding="utf-8" when appropriate for text files.

List Comprehension

Create lists using concise expressions.

List comprehension provides a short and readable way to create a new list from an existing iterable such as a list, tuple, range, or string. Instead of writing a separate for loop and using append(), the expression and loop can be written in a single line.

Traditional Approach

Normally, we create an empty list and use a for loop to add each calculated value to it.


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

squares = []

for number in numbers:
squares.append(number * number)

print(squares)
                  
Output:
[1, 4, 9, 16, 25]

Using List Comprehension

The same operation can be written more compactly using list comprehension. The expression number * number is applied to every item in numbers.


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

squares = [number * number for number in numbers]

print(squares)
                  
Output:
[1, 4, 9, 16, 25]

List Comprehension with Condition

A condition can also be added to a list comprehension. This allows us to include only the items that satisfy a particular condition.


numbers = range(1, 11)

even_numbers = [
number for number in numbers
if number % 2 == 0
]

print(even_numbers)
                  
Output:
[2, 4, 6, 8, 10]
Tip: List comprehension is useful when the operation is simple and easy to understand. If the logic becomes complicated, using a normal for loop may make the code easier to read.

Lambda Functions

Work with small anonymous functions.

A lambda function is a small anonymous function that can perform a simple operation without defining a function using def. It is generally used when a function is needed for a short operation, especially when working with functions such as sorted(), map(), and filter().

Basic Lambda Function

A lambda function can accept parameters and return the result of an expression. In the example below, the function receives x and returns its square.


square = lambda x: x * x

print(square(5))
                  
Output: 25

Lambda with Multiple Parameters

A lambda function can accept more than one parameter. Here, a and b are passed to the function and their values are added together.


add = lambda a, b: a + b

print(add(10, 20))
                  
Output: 30

Lambda with sorted()

Lambda functions are commonly used with sorted() when the sorting should be based on a particular value inside each item. In this example, students are sorted according to their marks.


students = [
  ("Arun", 80),
  ("Priya", 95),
  ("Kumar", 70)
]

students.sort(key=lambda student: student[1])

print(students)
                  
Output:
[('Kumar', 70), ('Arun', 80), ('Priya', 95)]
Tip: Lambda functions are best suited for short and simple operations. When the logic becomes complex or needs to be reused in multiple places, a normal function using def is usually clearer.

Decorators in Python

Add extra functionality to functions without modifying their original code.

Decorators are used to modify or extend the behavior of an existing function without changing its original implementation. They are commonly used when the same additional functionality needs to be applied to multiple functions.

Python treats functions as objects, which means a function can be passed as an argument to another function and can also be returned from a function. Decorators use this feature to wrap an existing function with additional behavior.

Simple idea: A decorator takes an existing function, adds some extra behavior around it, and returns the modified function.

Basic Structure of a Decorator

A decorator normally contains another function inside it. The inner function is responsible for adding the extra behavior and then calling the original function.


def decorator_function(original_function):

  def wrapper():
      # Additional behavior
      original_function()

  return wrapper
                  

Here, original_function represents the function that we want to modify, while wrapper() contains the additional behavior.

Creating a Simple Decorator

Consider a situation where several functions need to display a welcome message before they execute. Instead of repeating the same statement in every function, we can place it inside a decorator.


def welcome(func):

  def wrapper():
      print("Welcome")
      func()

  return wrapper


def message():
  print("Python Tutorial")


message = welcome(message)

message()
                  
Output:
Welcome
Python Tutorial

The welcome() function receives message as an argument. It creates a wrapper function that prints Welcome and then calls the original message() function.

Using the @ Decorator Syntax

Python provides a shorter syntax for applying a decorator. Instead of assigning the decorated function manually, we can place the decorator name above the function using the @ symbol.


def welcome(func):

  def wrapper():
      print("Welcome")
      func()

  return wrapper


@welcome
def message():
  print("Python Tutorial")


message()
                  
Output:
Welcome
Python Tutorial

The statement @welcome tells Python to apply the welcome decorator to the message() function. Internally, Python performs the equivalent of:

message = welcome(message)

Decorator with Function Arguments

Decorators can also be used with functions that accept arguments. In this case, the wrapper should accept the required arguments and pass them to the original function.


def display(func):

  def wrapper(name):
      print("Student Information")
      func(name)

  return wrapper


@display
def student(name):
  print("Student:", name)


student("Arun")
                  
Output:
Student Information
Student: Arun

Practical Uses of Decorators

Decorators are widely used in Python applications and frameworks because they allow common functionality to be added without repeating code.

Logging: Record when a function is called and what operations it performs.
Authentication: Check whether a user is authorized to access a function or resource.
Execution Time: Measure how long a function takes to complete.
Input Validation: Check function arguments before executing the main function.
Caching: Store previously calculated results to avoid unnecessary processing.

Decorator Best Practices

Keep decorators focused: A decorator should generally perform one specific task.
Use functools.wraps(): Preserve the original function's name and documentation.
Use meaningful names: Give decorators names that clearly describe the additional behavior.
Avoid unnecessary decorators: Use them when they actually make the code cleaner or reusable.

Generators & Iterators

Understand iteration and memory-efficient value generation.

Iterators and generators are used in Python to process values one at a time. They are especially useful when working with collections, sequences and large amounts of data.

An iterator keeps track of its current position and provides the next value when requested. A generator is a simple way to create an iterator using the yield keyword.

Simple idea: Iterators allow values to be processed one at a time, while generators provide an easy and memory-efficient way to create those values.

What Is an Iterable?

An iterable is an object whose elements can be accessed one at a time. Python provides many built-in iterable objects such as lists, tuples, strings, sets and dictionaries.

A for loop can be used to automatically iterate through the values of an iterable.


numbers = [10, 20, 30]

for number in numbers:
    print(number)
                  
Output:
10
20
30

The list numbers is an iterable because Python can access each of its elements one by one.

Iterator

An iterator is an object that keeps track of its current position while going through a collection. The iter() function can be used to create an iterator from an iterable.


numbers = [10, 20, 30]

iterator = iter(numbers)

print(next(iterator))
print(next(iterator))
print(next(iterator))
                  
Output:
10
20
30

The next() function requests the next value from the iterator. Each time it is called, the iterator moves to the next element.

Important: If next() is called after all values have been consumed, Python raises a StopIteration exception.

Using an Iterator with a for Loop

Although next() can be used manually, Python's for loop automatically handles the iteration process.


numbers = [10, 20, 30]

iterator = iter(numbers)

for number in iterator:
    print(number)
                  
Output:
10
20
30

Generator

A generator is a special type of iterator that produces values one at a time. Generators are created using a function containing the yield keyword.

Unlike return, which ends a function completely, yield pauses the function and remembers its current state. When the next value is requested, the function continues from where it stopped.


def numbers():
  yield 1
  yield 2
  yield 3


for number in numbers():
  print(number)
                  
Output:
1
2
3

Using next() with a Generator

Since a generator is an iterator, we can also use next() to request each value individually.


def numbers():
  yield 10
  yield 20
  yield 30


values = numbers()

print(next(values))
print(next(values))
print(next(values))
                  
Output:
10
20
30

yield vs return

Both yield and return can produce a value from a function, but they behave differently.

Feature return yield
Purpose Returns a result and ends the function. Produces a value and pauses the function.
Execution Function stops immediately. Function can continue from where it stopped.
Values Normally returns a result at a time. Can produce multiple values one at a time.
Memory May require storing a complete collection if multiple values are created. Produces values only when required.

Generator Expression

Python also provides generator expressions, which have a syntax similar to list comprehensions. The main difference is that a generator expression produces values one at a time instead of creating the complete list immediately.


numbers = (number * number for number in range(1, 6))

for number in numbers:
print(number)
                  
Output:
1
4
9
16
25
Remember: Square brackets [] create a list comprehension, while parentheses () create a generator expression.

Why Use Generators?

Generators are useful when a program needs to process a large amount of data. Instead of creating and storing every value at once, a generator produces each value only when it is needed.

Memory efficient: Values are generated one at a time instead of storing the complete collection in memory.
Large data: Useful when processing large files, datasets or sequences.
Lazy evaluation: Values are calculated only when they are requested.
Simple implementation: The yield keyword makes it easier to create custom iterators.

Iterable vs Iterator vs Generator

Concept Description Example
Iterable An object whose elements can be accessed one at a time. List, tuple, string
Iterator An object that produces the next value using next(). iter(list)
Generator A convenient way to create an iterator using yield. Generator function

Generator & Iterator Best Practices

Use generators for large data: They can reduce memory usage by producing values when needed.
Use yield for sequences: It is useful when a function needs to produce multiple values gradually.
Use for loops when possible: Python automatically handles the iterator process and StopIteration.
Choose lists when necessary: If all values need to be accessed repeatedly, a list may be more convenient than a generator.

Virtual Environments

Isolate project dependencies and Python packages.

A virtual environment is an isolated environment used to install Python packages for a specific project. It helps prevent package conflicts between different Python projects.

For example, one project may require a particular version of a library, while another project may require a different version. Virtual environments allow both projects to maintain their own packages.

Simple idea: Each project can have its own Python packages and dependencies without affecting other projects.

Creating a Virtual Environment

Python provides the venv module to create a virtual environment.

python -m venv myenv

Here, myenv is the name of the virtual environment. You can choose a different name if required.

Activating on Windows

After creating the environment, activate it using the following command:

myenv\Scripts\activate

Once activated, packages installed using pip will be installed inside this virtual environment.

Activating on Linux / macOS

source myenv/bin/activate

Installing a Package

After activating the environment, you can install packages using pip.

pip install requests

The requests package will be installed in the active virtual environment rather than globally.

Saving Project Dependencies

The installed packages can be saved in a requirements.txt file. This makes it easier to recreate the same environment on another computer.

pip freeze > requirements.txt

Another developer can install all the packages listed in the file using:

pip install -r requirements.txt

Deactivating the Environment

When you finish working on the project, you can leave the virtual environment using the deactivate command.

deactivate

Basic Virtual Environment Workflow

1. Create: Create a virtual environment using python -m venv myenv.
2. Activate: Activate the environment before installing packages.
3. Install: Install the required packages using pip install.
4. Work: Develop and run your Python project inside the environment.
5. Deactivate: Deactivate the environment when you finish.