Python: A Comprehensive Tutorial
Last Updated: 12-08-2024

I create cross platform mobile apps with AI functionalities. Currently a PhD Scholar at Indira Gandhi Delhi Technical University for Women, Delhi. M.Tech in Artificial Intelligence (AI).
Python is a high-level, interpreted, interactive and object-oriented scripting and general-purpose programming language.
Python was developed by Guido van Rossum, a Dutch programmer, currently a Distinguished Engineer at Microsoft.
Python supports multi-paradigm:
Object-oriented: Define classes and create objects that encapsulate data and methods.
Procedural (imperative): writing sequences of instructions and procedures to manipulate data and control program flow
Functional: writing functions as first-class objects that can be passed around and combined to perform operations
Structured: breaking down programs into functions and using control flow constructs to manage execution
Reflective: to inspect and modify the program’s structure and behavior at runtime.
Pre-Requisite:
You will need a computer and python 3.7 on it.
File Extensions
Filename extensions: .py, .pyw, .pyz, .pyi, .pyc, .pyd
All these file extensions are used for different purposes.
.py: This is the standard file extension for Python source code files. They contain plain text code written in Python and are executed using the Python interpreter.
.pyw: This extension is used for Python scripts that are run in a Windows environment but should not open a command-line window when executed. It’s typically used for GUI applications that need to run without a terminal window.
.pyz: This extension represents a Python zip archive. It contains a collection of Python files bundled together in a compressed format, often used for distributing Python applications. These archives are self-contained and can be executed as standalone programs if they include a main.py file.
.pyi: These files are used for type hinting in Python. They are "stub" files that provide type information for Python code, useful for type checkers and IDEs to help with code analysis and autocompletion.
.pyc: These are compiled Python files. When a .py file is executed, Python compiles it into bytecode, which is stored in a .pyc file. These files are used to make program execution faster, as Python can skip the compilation step if the .pyc file is up-to-date.
.pyd: This is a dynamic library file used in Python, similar to a shared library or DLL in other languages. It contains compiled C or C++ code that can be imported and used in Python programs. These are often used to interface with C/C++ code from Python.
Python Basics
Identifiers
An identifier is a name given to entities like class, functions, variables, etc. It helps to differentiate one entity from another.
Rules for writing Identifiers
Identifiers can be a combination of letters in lowercase (a to z) or uppercase (A to Z) or digits (0 to 9) or an underscore . Names like myClass, var1 and print_this_to_screen, all are valid example.
# Valid Identifiers id = 24An identifier cannot start with a digit. 1variable is invalid, but variable1 is a valid name.
# Invalid Identifiers 1id = 24 # Gives ErrorKeywords cannot be used as identifiers.
# Using keywords as identifier will give error def main(): if = 10 # 'if' is a reserved keyword print(if) main()Output:

We cannot use special symbols like !, @, #, $, % etc. in our identifier.
def main(): @value = 100 # '@' is not allowed in identifiers print(@value) main()Output:

An identifier can be of any length.
very_long_identifier_name_that_exceeds_normal_length_limits = "How you doing?" print(very_long_identifier_name_that_exceeds_normal_length_limits)
Tips
Python is a case-sensitive language.
Always give the identifiers a name that makes sense.
Multiple words can be separated using an underscore, like this_is_a_long_variable.
Statement, Indentation and Comments
Statement
A statement is an instruction that the Python interpreter can execute
Single Line Statement
# Assignment Statements: Assign values to variables.
x = 10
# Expression Statement: Evaluate an expression and then execute it.
y = x + 3
Multi-Line Statement
We can use a backslash (\) to indicate that a statement continues on the next line.
y = 1 + 2 + \
3 + 4 + \
5 + 6
Python implicitly continues a statement across multiple lines when enclosed in parentheses (), brackets [], or braces {}. This is often preferred for better readability.
result = (
1 + 2 + 3 + 4 +
5 + 6 + 7 + 8 +
9 + 10
)
For multi-line strings, we can use triple quotes (''' or """). This is useful for long strings or docstrings.
long_string = """This is a long string
that spans multiple lines.
We can include line breaks and other formatting here."""
Multiple Statements in single line
We can also put multiple statements in a single line using semicolons,
a=1; b=2;c=3;
Indentation
Python uses indentation to define a block of code.
A code block (body of a function, loop, etc.) starts with indentation and ends with the first unindented line. The amount of indentation is up to us, but it must be consistent throughout that block.
# Example of indentation in Python
# Function definition with indentation
def greet(name):
# Indented block for the function body
if name:
# Further indentation for the if block
print(f"Hello, {name}!")
else:
# Further indentation for the else block
print("Hello, stranger!")
# Calling the function
greet("Alice")
Comments
In Python, comments are used to explain code, making it easier to understand for other programmers. They are ignored by the Python interpreter and do not affect the execution of the code.
Single Line Comments
Single-line comments start with the hash mark (#). Everything after the # on that line is considered a comment and is ignored by the interpreter.
# This is a single-line comment
x = 10 # This is an inline comment
Multi-line Comments
Python does not have a specific syntax for multi-line comments. Instead, multi-line comments are typically written using a series of single-line comments or a string literal. Though string literals (triple quotes) are often used for multi-line comments, they are technically string literals and not true comments.
# This is a multi-line comment
# It spans several lines
# and is created using consecutive single-line comments
"""
This is a multi-line comment
using triple quotes. This is actually a string literal,
but it can be used to comment out code or add documentation.
"""
Docstrings are used to document modules, classes, and functions. They are written using triple quotes and should be placed right after the definition of the module, class, or function.
def add(a, b):
""" Function to add two numbers and return the result """
return a + b
Variables
A variable is a named location used to store data in the memory. It is helpful to think of variables as a container that holds data that can be changed later in the program.
num = 15
Here, we have created a variable named 'num'. We have assigned the value 15 to the variable. We can think of variables as a bag to store books in it and that book can be replaced at any time. The assignment operator = is used to assign a value to a variable.
Example 1: Declaring and assigning value to variables
website = "www.google.com"
print(website)
Example 2: Changing the value of a variable
website = "www.google.com"
print(website)
# assinging a new value to website
website = "www.bing.com"
print(website)
Example 3: Assigning multiple values to multiple variables
a, b, c = 10, 3.2, "Hello"
x = y = z = "same value"
print(a)
print(b)
print(c)
print(x,y,z)
Example 4: Declaring and assigning value to constant
Python does not have built-in support for constants, so it's a convention to use uppercase variable names to indicate that a value is intended to be constant.
PI = 3.14
GRAVITY = 9.8
Data Types
Data types represent the kind of value a variable can hold. Python supports a variety of data types, each serving different purposes.
There are two types of Data Types
Built-in Data Types: These are pre-built-in python by default.
User-Defined Data Types: These are defined by users.
Built-in Data Types
Numeric Types
int: Integer type
float: Floating-point numbers (decimals).
complex: Complex numbers with a real and imaginary part.
# integer type
a = 10
# Float type
b = -0.001
# complex type
c = 2 + 3j
Sequence Types
str: Strings, which are sequences of characters.
list: Ordered, mutable sequences of items.
tuple: Ordered, immutable sequences of items.
s = "Hello, world!" # String
numbers = [1, 2, 3, 4, 5] # List
point = (10, 20, 30) # Tuple
Mapping Types
- dict: Dictionaries, which are collections of key-value pairs
# dictionary
person = {"name": "Alice", "age": 30, "city": "New York"}
Set Types
set: Unordered collections of unique items
frozenset: Immutable sets.
unique_numbers_set = {1, 2, 3, 4, 5} # set
immutable_set = frozenset([1, 2, 3, 4, 5]) # Frozen set
Boolean Types
- bool: Boolean values, which can be True or False
is_active = True
is_empty = False
Binary Types
byte: Immutable sequences of bytes
bytearray: Mutable sequences of bytes
memoryview: A view object that exposes an array’s data buffer.
b = b"Hello" # byte
ba = bytearray(b"Hello") # bytearray
mv = memoryview(b"Hello") # memoryview
User-Defined Data Types
User-defined data types in Python are custom types that we create to model specific kinds of data in our programs. They are typically created using classes, allowing us to define the structure and behavior of these types.
Classes: Define complex data types with attributes and methods.
Named Tuples: Provide immutable data structures with named fields.
Data Classes: Simplify the creation of classes for storing data with automatic methods.
Enumerations: Define a set of named constants.
Custom Iterators: Implement custom iteration logic using __iter__() and __next__().
Custom Context Managers: Manage resources using __enter__() and __exit__() with the with statement.
Classes
A class is a blueprint for creating objects. Classes can have attributes (data) and methods (functions) to define their behavior.
class Car:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def display_info(self):
return f"{self.year} {self.make} {self.model}"
# Create an instance of the Car class
my_car = Car("Toyota", "Corolla", 2021)
print(my_car.display_info()) # Output: 2021 Toyota Corolla
Named Tuples
Named tuples are a type of tuple with named fields, which can be accessed like attributes. They are useful for simple data structures that don’t need methods.
from collections import namedtuple
Person = namedtuple('Person', ['name', 'age'])
# Create an instance of the Person named tuple
p = Person(name="Alice", age=30)
print(p.name) # Output: Alice
print(p.age) # Output: 30
Data Classes
Data classes, introduced in Python 3.7, simplify the creation of classes for storing data by automatically generating special methods such as __init__, __repr__, and __eq__.
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
# Create an instance of the Person data class
p = Person(name="Alice", age=30)
print(p) # Output: Person(name='Alice', age=30)
Custom Enumerations
Enumerations (enums) are a way to define a set of named values. They are used to create a collection of constants.
from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
# Access enum members
print(Color.RED) # Output: Color.RED
print(Color.RED.name) # Output: RED
print(Color.RED.value) # Output: 1
Custom Iterators
Custom iterators are objects that implement the iterator protocol, consisting of __iter__() and __next__() methods.
class Counter:
def __init__(self, low, high):
self.current = low
self.high = high
def __iter__(self):
return self
def __next__(self):
if self.current > self.high:
raise StopIteration
else:
self.current += 1
return self.current - 1
# Use the custom iterator
for num in Counter(3, 8):
print(num) # Output: 3 4 5 6 7 8
Custom Context Manager
Custom context managers are used to manage resources efficiently and are implemented using the __enter__() and __exit__() methods. They are typically used with the 'with' statement.
class FileManager:
def __init__(self, filename):
self.filename = filename
def __enter__(self):
self.file = open(self.filename, 'w')
return self.file
def __exit__(self, exc_type, exc_value, traceback):
self.file.close()
# Use the custom context manager
with FileManager('example.txt') as file:
file.write('Hello, world!')
These user-defined data types enable one to model and manage complex data in a way that is tailored to the application's needs.
Keywords
Keywords are reserved words in python. Keywords must be used for the purpose it is made for.
Keyword cannot be used as a variable name, function name or any other identifier. They are used to define the syntax and structure of the Python language.
All keywords are case sensitive.
There are 33 keywords in Python 3.7.
All the keywords except True, False and None are in lowercase and they must be written as they are.
Let's see each keyword one by one and their use case.
True: Boolean value representing true.
flag = True
False: Boolean value representing false
flag = False
None: Represents the absence of a value or a null value.
result = None
and: Logical operator that returns True if both operands are true.
if a > 0 and b > 0:
print("Both are positive")
as: Used to create an alias while importing modules or handling exceptions.
import numpy as np
in: Checks if a value exists within an iterable or performs membership tests.
if x in [1, 2, 3]:
print("x is in the list")
is: Tests for object identity, whether two variables point to the same object.
if a is b:
print("a and b are the same object")
not: Logical operator that negates a boolean expression.
if not x:
print("x is False or None")
or: Logical operator that returns True if at least one of the operands is true.
if a > 0 or b > 0:
print("At least one is positive")
from: Used to import specific parts of a module.
from math import sqrt
with: Used to wrap the execution of a block of code within methods defined by the context manager. It ensures that resources are properly managed.
with open("file.txt", "r") as file:
contents = file.read()
import: Used to import modules into the current namespace.
import math
global: Declares a global variable inside a function.
def my_function():
global x
x = 10
nonlocal: Refers to a variable in the nearest enclosing scope that is not global.
def outer_function():
x = 10
def inner_function():
nonlocal x
x = 20
inner_function()
print(x)
class: Used to define a new class
class MyClass:
pass
assert: Used for debugging purposes to test if a condition is true. Raises an AssertionError if the condition is false.
assert x > 0, "x must be positive"
async: used to define a coroutine. A coroutine is a special type of function that can pause and resume its execution. Coroutines are defined using the "async def" syntax.
async def fetch_data():
print("Fetching data...")
await asyncio.sleep(2) # Simulate a network delay
print("Data fetched")
return "some data"
await: used inside an async function to pause its execution until a coroutine or an awaitable object completes. await can only be used within async functions. It effectively tells the Python interpreter to wait for the result of the coroutine before continuing
import asyncio
async def main():
result = await fetch_data() # Awaiting the coroutine
print(result)
asyncio.run(main())
Use async def to define a coroutine. This function will return a coroutine object, which can be awaited.
Inside an async function, use await to pause execution until the awaited coroutine completes. This allows other tasks to run concurrently.
Use asyncio.run() to execute the top-level coroutine. This function runs the event loop, executes the coroutine, and closes the loop when done.
if: Used to start a conditional statement.
if x > 0:
print("Positive")
else: Used in conditional statements and loops. Executes a block of code if the condition in if or while is false, or when a loop terminates normally.
if x > 0:
print("Positive")
else:
print("Non-positive")
elif: Used in conditional statements as an abbreviation for "else if"
if x > 0:
print("Positive")
elif x < 0:
print("Negative")
else:
print("Zero")
break: Exits the closest enclosing loop.
for i in range(10):
if i == 5:
break
continue: Skips the rest of the code inside the current loop iteration and proceeds to the next iteration.
for i in range(10):
if i % 2 == 0:
continue
print(i)
pass: A null statement used as a placeholder. It does nothing and is used when a statement is required syntactically but we don’t want to execute any code.
def function_that_does_nothing():
pass
def: Used to define a function.
def my_function(param1):
return param1 * 2
lambda: Creates an anonymous function (a function without a name).
square = lambda x: x * x
del: Deletes an object or a reference to an object.
del my_list
for: Used to create a loop that iterates over a sequence (like a list, tuple, or range).
for i in range(5):
print(i)
while: Creates a loop that continues as long as a condition is true.
while x > 0:
print(x)
x -= 1
raise: Raises an exception
raise ValueError("A value error occurred")
return: Exits a function and optionally returns a value.
def add(a, b):
return a + b
yield: Used in a generator function to yield a value and pause the function’s execution, allowing it to resume later.
def count_up_to(max):
count = 1
while count <= max:
yield count
count += 1
try: Used to start a block of code that will be tested for exceptions.
except: Used to catch and handle exceptions raised in a try block.
finally: Used to define a block of code that will be executed no matter what, regardless of whether an exception was raised or not.
try:
file = open('example.txt', 'r')
# Perform file operations
except FileNotFoundError:
print("File not found")
finally:
file.close()
print("File closed")
Type Conversion and Type Casting
Type conversion refers to the process of converting a value from one type to another type. This can be done implicitly or explicitly.
Implicit Conversion: Python automatically converts between types when it's safe and logical to do so. For example, when performing arithmetic operations with integers and floats, Python will implicitly convert integers to floats to ensure accuracy
Explicit Conversion: Also known as type casting, explicit conversion is when we manually convert between types using functions like int(), float(), str(), etc.
Type Conversion Examples
Python provides functions to convert between different data types
To int
# From float to int
num_float = 12.34
num_int = int(num_float) # 12
# From string to int
num_str = "123"
num_int = int(num_str) # 123
# Note: Strings should represent integers to convert successfully.
# num_str = "12.34" would raise a ValueError
To Float
# From int to float
num_int = 12
num_float = float(num_int) # 12.0
# From string to float
num_str = "12.34"
num_float = float(num_str) # 12.34
To str
# From int to string
num_int = 123
num_str = str(num_int) # "123"
# From float to string
num_float = 12.34
num_str = str(num_float) # "12.34"
To list
# From string to list of characters
str_data = "hello"
list_data = list(str_data) # ['h', 'e', 'l', 'l', 'o']
# From tuple to list
tuple_data = (1, 2, 3)
list_data = list(tuple_data) # [1, 2, 3]
To tuple
# From list to tuple
list_data = [1, 2, 3]
tuple_data = tuple(list_data) # (1, 2, 3)
# From string to tuple of characters
str_data = "hello"
tuple_data = tuple(str_data) # ('h', 'e', 'l', 'l', 'o')
To set
# From list to set
list_data = [1, 2, 2, 3]
set_data = set(list_data) # {1, 2, 3}
# From string to set of characters
str_data = "hello"
set_data = set(str_data) # {'h', 'e', 'l', 'o'}
To dict
# From list of tuples to dict
list_data = [("a", 1), ("b", 2), ("c", 3)]
dict_data = dict(list_data) # {'a': 1, 'b': 2, 'c': 3}
# From list of lists to dict
list_data = [["a", 1], ["b", 2], ["c", 3]]
dict_data = dict(list_data) # {'a': 1, 'b': 2, 'c': 3}
Python I/O and File Handling
In Python, input and output operations are fundamental for interacting with users and handling data.
Input
To get input from the user, we use the input() function. This function reads a line from input (usually from the keyboard) and returns it as a string.
user_input = input("Enter something: ")
print(f"You entered: {user_input}")
Output
To display output, we use the print() function. It sends the specified text or data to the standard output (usually the console).
print("Hello, World!")
File Modes
File modes determine how we can interact with a file when we open it. These modes specify whether we want to read, write, or append to a file and if we want to handle it as text or binary data.
'r' – Read mode (default).
'w' – Write mode (creates a new file or truncates an existing file).
'a' – Append mode (writes to the end of the file).
'b' – Binary mode (for binary files).
Reading and Writing Files
We can read from and write to files using the open() function along with read(), write(), and other methods.
# Reading from a File
with open('example.txt', 'r') as file:
content = file.read()
print(content)
# Writing to a file
with open('example2.txt', 'w') as file2:
file2.write("Hello, file!")
Handling Files with Context Managers
Using with (context managers) ensures that files are properly closed after their block of code is executed, even if an error occurs.
with open('example.txt', 'r') as file:
data = file.read()
# File is automatically closed here
Reading File Line by Line
with open('example.txt', 'r') as file:
for line in file:
print(line.strip())
Reading and Writing Binary Files
To handle binary files, use the 'b' mode in open(). This is useful for files such as images or executable files.
Reading Binary Files
with open('example.jpg', 'rb') as file:
data = file.read()
Writing Binary Files
with open('output.bin', 'wb') as file:
file.write(data)
Reading and Writing CSV files
Python's csv module is handy for handling CSV files.
Reading CSV files
import csv
with open('example.csv', 'r') as file:
reader = csv.reader(file)
for row in reader:
print(row)
Writing CSV files
import csv
data = [
["Name", "Age"],
["Alice", 30],
["Bob", 25]
]
with open('example.csv', 'w', newline='') as file:
writer = csv.writer(file)
writer.writerows(data)
Reading and Writing JSON files
The json module helps in working with JSON data.
Reading JSON files
import json
with open('example.json', 'r') as file:
data = json.load(file)
print(data)
Writing JSON files
import json
data = {"name": "Alice", "age": 30}
with open('example.json', 'w') as file:
json.dump(data, file)
Reading and Writing Pickle Module
The pickle module is used for serializing and deserializing Python objects.
Pickling (writing) an object
import pickle
data = {'key': 'value', 'number': 42}
with open('example.pkl', 'wb') as file:
pickle.dump(data, file)
Unpickling (reading) an object
import pickle
with open('example.pkl', 'rb') as file:
data = pickle.load(file)
print(data)
Using io Module for In-Memory Streams
The io module provides tools for handling in-memory streams.
StringIO (in-memory text stream)
from io import StringIO
# Create an in-memory text stream
sio = StringIO("Initial data")
print(sio.read()) # Read the content
sio.write(" More data") # Write to the stream
sio.seek(0) # Rewind to the beginning
print(sio.read()) # Read again
BytesIO (in-memory binary stream)
from io import BytesIO
# Create an in-memory binary stream
bio = BytesIO(b"Initial binary data")
print(bio.read()) # Read the content
bio.write(b" More binary data") # Write to the stream
bio.seek(0) # Rewind to the beginning
print(bio.read()) # Read again
Files Path
Handling file paths in Python involves working with pathnames to ensure our code can correctly locate and manipulate files and directories. Python provides several modules to manage file paths effectively: os.path, and pathlib.
Using os.path
import os
# Join paths
path = os.path.join('folder', 'subfolder', 'file.txt')
# Check if a path exists
print(os.path.exists(path))
# Get the absolute path
print(os.path.abspath(path))
# Get the directory name
print(os.path.dirname(path))
# Get the base name (file name)
print(os.path.basename(path))
# Split the path into directory and file name
print(os.path.split(path))
# Check if it's a file
print(os.path.isfile(path))
# Check if it's a directory
print(os.path.isdir(path))
Using pathlib
The pathlib module, introduced in Python 3.4, provides an object-oriented interface for handling file system paths.
from pathlib import Path
# Create a Path object
path = Path('folder') / 'subfolder' / 'file.txt'
# Check if a path exists
print(path.exists())
# Get the absolute path
print(path.resolve())
# Get the directory name
print(path.parent)
# Get the file name
print(path.name)
# Split the path into directory and file name
print(path.parts)
# Check if it's a file
print(path.is_file())
# Check if it's a directory
print(path.is_dir())
Operators
Operators in Python are special symbols or keywords used to perform operations on values and variables. They can be categorized into several types based on their functionality.
Arithmetic Operators: Perform basic mathematical operations.
Comparison Operators: Compare two values.
Logical Operators: Perform logical operations.
Assignment Operators: Assign and update variable values.
Bitwise Operators: Perform bit-level operations.
Membership Operators: Check membership in sequences.
Identity Operators: Compare object identities
Arithmetic Operators
Addition (+): Adds two operands.
Subtraction (-): Subtracts the second operand from the first.
Multiplication (*): Multiplies two operands.
Division (/): Divides the first operand by the second and returns a float.
Floor Division (//): Divides the first operand by the second and returns the largest integer less than or equal to the result.
Modulus (%): Returns the remainder of the division
Exponentiation (**): Raises the first operand to the power of the second.
print(5 + 3) # 8
print(5 - 3) # 2
print(5 * 3) # 15
print(5 / 2) # 2.5
print(5 // 2) # 2
print(5 % 2) # 1
print(5 ** 2) # 25
In the above code:
print(5 + 3) adds 5 and 3, resulting in 8.
print(5 - 3) subtracts 3 from 5, giving 2.
print(5 \ 3) multiplies 5 by 3, resulting in 15.*
print(5 / 2) divides 5 by 2, giving 2.5 (a float).
print(5 // 2) performs integer division, which divides 5 by 2 and drops the decimal part, resulting in 2.
print(5 % 2) finds the remainder when 5 is divided by 2, which is 1.
print(5 ** 2) raises 5 to the power of 2, resulting in 25.
Comparison Operators
Comparison operator is used to compare two values and gives Boolean value in return as True or False.
Equal to (==): Checks if two operands are equal.
Not equal to (!=): Checks if two operands are not equal.
Greater than (>): Checks if the first operand is greater than the second.
Less than (<): Checks if the first operand is less than the second.
Greater than or equal to (>=): Checks if the first operand is greater than or equal to the second.
Less than or equal to (<=): Checks if the first operand is less than or equal to the second.
print(5 == 3) # False
print(5 != 3) # True
print(5 > 3) # True
print(5 < 3) # False
print(5 >= 3) # True
print(5 <= 3) # False
In the above code
print(5 == 3) checks if 5 is equal to 3. Since they are not equal, the result is False.
print(5 != 3) checks if 5 is not equal to 3. Since they are not equal, the result is True.
print(5 > 3) checks if 5 is greater than 3. Since 5 is indeed greater, the result is True.
print(5 < 3) checks if 5 is less than 3. Since 5 is greater, the result is False.
print(5 >= 3) checks if 5 is greater than or equal to 3. Since 5 is greater, the result is True.
print(5 <= 3) checks if 5 is less than or equal to 3. Since 5 is greater, the result is False.
Logical Operators
Used to perform logical operations and gives True or False in return
And (and): Returns True if both operands are true.
Or (or): Returns True if at least one operand is true.
Not (not): Returns True if the operand is false.
print(True and False) # False
print(True or False) # True
print(not True) # False
In the above code
print(True and False) uses the and operator, which returns True only if both values are True. Since one value is False, the result is False.
print(True or False) uses the or operator, which returns True if at least one value is True. Here, one value is True, so the result is True.
print(not True) uses the not operator to invert the value. Since not True is False, the result is False.
Assignment Operators
Assignment (=): Assigns the value on the right to the variable on the left.
Add and assign (+=): Adds and assigns.
Subtract and assign (-=): Subtracts and assigns.
Multiply and assign (*=): Multiplies and assigns.
Divide and assign (/=): Divides and assigns.
Floor divide and assign (//=): Floor divides and assigns.
Modulus and assign (%=): Modulus and assigns.
Exponentiate and assign (**=): Exponentiates and assigns.
a = 5
a += 3 # Equivalent to a = a + 3
a -= 3 # Equivalent to a = a - 3
a *= 3 # Equivalent to a = a * 3
a /= 3 # Equivalent to a = a / 3
a //= 3 # Equivalent to a = a // 3
a %= 3 # Equivalent to a = a % 3
a **= 3 # Equivalent to a = a ** 3
In the above code, a starts with the value 5. The code then uses shorthand operators to update a:
a += 3 adds 3 to a, so a becomes 8.
a -= 3 subtracts 3 from a, returning it to 5.
a \= 3 multiplies a by 3, changing it to 15.
a /= 3 divides a by 3, resulting in 5.0 (a float).
a //= 3 performs integer division by 3, changing a to 1.0 (still a float).
a %= 3 calculates the remainder of a divided by 3, making a equal to 1.0.
a *= 3 raises a to the power of 3, resulting in 1.0 (since 1.0 ** 3 is 1.0).
Bitwise Operators
And (&): Performs a bitwise AND operation.
Or (|): Performs a bitwise OR operation.
Xor (^): Performs a bitwise XOR operation.
Complement (~): Performs a bitwise NOT operation.
Left Shift (<<): Shifts bits to the left.
Right Shift (>>): Shifts bits to the right.
print(5 & 3) # 1
print(5 | 3) # 7
print(5 ^ 3) # 6
print(~5) # -6
print(5 << 1) # 10
print(5 >> 1) # 2
In the above code, bitwise operations are done:
print(5 & 3) performs a bitwise AND between 5 (which is 101 in binary) and 3 (which is 011 in binary), resulting in 001 in binary, which is 1.
print(5 | 3) performs a bitwise OR, combining 101 and 011 to get 111 in binary, which is 7.
print(5 ^ 3) performs a bitwise XOR, where 101 XOR 011 results in 110 in binary, which is 6.
print(~5) performs a bitwise NOT, which inverts 101 to ...11111010 in binary (the two's complement representation), resulting in -6.
print(5 << 1) performs a left shift, moving 101 one position to the left to get 1010 in binary, which is 10.
print(5 >> 1) performs a right shift, moving 101 one position to the right to get 10 in binary, which is 2.
Membership Operators
In (in): Returns True if a value is found in a sequence.
Not in (not in): Returns True if a value is not found in a sequence.
print(5 in [1, 2, 3, 4, 5]) # True
print(6 not in [1, 2, 3, 4, 5]) # True
In the above code, print(5 in [1, 2, 3, 4, 5]) checks if the number 5 is present in the list [1, 2, 3, 4, 5]. Since 5 is indeed in the list, it prints True. On the other hand, print(6 not in [1, 2, 3, 4, 5]) checks if the number 6 is not in the list. Since 6 is not in the list, it prints True.
Identity Operators
Is (is): Returns True if both variables point to the same object.
Is not (is not): Returns True if both variables point to different objects.
a = [1, 2, 3]
b = a
print(a is b) # True
print(a is not b) # False
In the above code, a is a list containing the elements [1, 2, 3]. The variable b is then set to refer to the same list as a. When we check a is b, it returns True because both a and b point to the exact same list in memory. Therefore, they are the same object. Conversely, a is not b returns False because a and b are indeed the same object, so it is not true that they are different.
If else and elif
If, elif, and else are used to make decisions and control the flow of our program based on conditions.
If Statement: The if statement evaluates a condition and executes the block of code that follows if the condition is True.
Elif statement: The elif (short for "else if") statement allows us to check multiple conditions. It follows an if statement and runs only if the preceding if (or elif) condition was False.
Else statement: The else statement runs a block of code if none of the preceding if or elif conditions are True. It’s optional and should be the final statement in the conditional chain.
score = 85
if score >= 90:
print("Grade: A")
elif score >= 80:
print("Grade: B")
elif score >= 70:
print("Grade: C")
elif score >= 60:
print("Grade: D")
else:
print("Grade: F")
This code assigns a score of 85 to a variable and then uses a series of conditional statements to determine and print the corresponding grade. It first checks if the score is 90 or above, then 80 or above, and so on. Since 85 is greater than 80 but less than 90, it prints "Grade: B" and stops checking further conditions. If the score didn’t meet any of the higher thresholds, the code would continue to check lower ranges or default to "Grade: F" if none of the conditions were met.
Loops
Loops are used to execute a block of code repeatedly based on a condition or a sequence.
For Loop
The for loop iterates over a sequence (like a list, tuple, or string) or other iterable objects.
SYNTAX:
for variable in sequence:
# Code to execute on each iteration
for i in range(5):
print(i)
This loop will print the numbers 0 through 4. The range(5) generates a sequence of numbers from 0 to 4.
For Loop with List
fruits = ['apple', 'banana', 'cherry']
for fruit in fruits:
print(fruit)
This will print each fruit in the list: apple, banana, cherry.
For Loop with Tuple
fruits = ('apple', 'banana', 'cherry')
for fruit in fruits:
print(fruit)
This will print each fruit in the tuple (which is an immutable data structure): apple, banana, cherry.
For Loop with String
word = 'hello'
for letter in word:
print(letter)
This will print each letter in the string: h, e, l, l, o.
For loop with Dictionary
When we work with dictionary, we can iterate over keys, values and key-value pairs.
Iterating Over Keys to print each key (Alice, Bob, Charlie).
student_grades = {'Alice': 90, 'Bob': 85, 'Charlie': 92}
for student in student_grades:
print(student)
Iterating Over values to print each value (90, 85, 92).
for grade in student_grades.values():
print(grade)
Iterating Over key-value pairs to print each key-value pair (Alice: 90, Bob: 85, Charlie: 92).
for student, grade in student_grades.items():
print(f"{student}: {grade}")
For Loop with Range
Iterate over a sequence of numbers generated by range().
In range function first argument is start value, second argument is stop value and third argument is step size. The function generates value from start_value till stop_value-1.
for i in range(0, 10, 2):
print(i)
range(0, 10, 2) generates numbers starting from 0 up to, but not including, 10, with a step of 2. The above code will print value 0, 2, 4, 6, 8.
For else Loop
We can use an else block with a for loop. The else block runs after the loop has completed all its iterations, but only if the loop was not terminated by a break statement.
for number in range(3):
print(number)
else:
print("Loop completed")
This loop iterates over the numbers 0, 1, and 2, printing each number. After completing the loop, it prints "Loop completed", since the loop ended normally without a break.
for number in range(3):
if number == 1:
break
print(number)
else:
print("Loop completed")
The loop prints 0, but when number is 1, the break statement is triggered. The else block is skipped because the loop did not finish all iterations normally (it was terminated by break).
While Loop
The while loop repeatedly executes a block of code as long as a given condition remains True. The while loop is flexible and useful when the number of iterations is not known beforehand.
count = 0
while count < 5:
print(count)
count += 1
The loop starts with count set to 0. As long as count is less than 5, it prints the current value of count. It then increments count by 1 on each iteration. When count reaches 5, the condition count < 5 becomes False, and the loop stops.
While else Loop
A while loop can be paired with an else block. The else block runs after the loop has completed all its iterations, but only if the loop was not terminated by a break statement.
count = 0
while count < 3:
print(count)
count += 1
else:
print("Loop finished")
The loop starts with count at 0 and runs while count is less than 3. It prints the current value of count and increments it. When count reaches 3, the condition count < 3 becomes False, and the loop terminates. The else block then executes, printing "Loop finished"
count = 0
while count < 5:
if count == 2:
break
print(count)
count += 1
else:
print("Loop finished")
The loop prints 0 and 1. When count is 2, the break statement is triggered, exiting the loop. The else block is skipped because the loop did not complete all iterations normally (it was exited by break).
break and continue statements
The break and continue statements are used to control the flow of loops by modifying their behavior under certain conditions.
Break statement
The break statement immediately terminates the loop, regardless of the loop’s condition. Execution continues with the code following the loop. It is used to exit a loop early when a certain condition is met.
for i in range(5):
if i == 3:
break
print(i)
This loop prints 0, 1, and 2. When i equals 3, the break statement is executed, and the loop exits. The number 3 is not printed, and the loop stops.
Continue statements
The continue statement skips the rest of the code inside the current iteration of the loop and proceeds to the next iteration. It does not terminate the loop.
It is used to commonly used to avoid executing certain code under specific conditions.
for i in range(5):
if i == 3:
continue
print(i)
This loop prints 0, 1, 2, and 4. When i equals 3, the continue statement skips the print(i) statement for that iteration, so 3 is not printed. The loop then continues with the next iteration.
Functions
Functions are reusable blocks of code designed to perform a specific task. They help organize code, avoid repetition, and make programs more modular and readable.
Function Definition
We can define a function using the keyword 'def', followed by the function name, parentheses, and a colon. The function body is indented.
def greet(name):
print(f"Hello, {name}!")
Explanation:
def starts the function definition.
greet is the name of the function.
name is a parameter (input) for the function.
The print statement inside the function prints a greeting message.
Function Call
To use a function, we can call it by its name and pass the required arguments (if any).
greet("Alice") # Output: Hello, Alice!
This calls the greet function with "Alice" as the argument. The function executes and prints "Hello, Alice!".
Return Value
Functions can return a value using the return statement. If no return statement is used, the function returns None by default.
def add(a, b):
return a + b
result = add(5, 3)
print(result) # Output: 8
The add function takes two parameters, a and b, and returns their sum. result stores the return value of add(5, 3), which is 8.
Parameters and Arguments
Parameters: Variables listed in the function definition.
Arguments: Values passed to the function when calling it.
def describe_person(name, age):
print(f"{name} is {age} years old.")
describe_person(age=25, name="Bob")
'age' and 'name' are passed as keyword arguments. We can change the order of arguments if while calling we use named arguments.
Pass by object reference
The concepts of "pass by value" and "pass by reference" describe how arguments are passed to functions. However, Python’s approach is a bit different from traditional pass-by-value or pass-by-reference models.
Pass by Value
In tradition programming languages, in pass-by-value, a copy of the argument's value is passed to the function. Changes made to the parameter inside the function do not affect the original argument.
Python does not use strict pass-by-value, but rather uses a model often described as "pass-by-object-reference" or "pass-by-assignment."
Pass by Reference
In traditional programming languages, in pass-by-reference, a reference (or pointer) to the argument is passed to the function. This means changes to the parameter will affect the original argument.
In python, it passes references to objects, but it does not provide direct access to the memory addresses like traditional pass-by-reference. Instead, it follows a model called "pass-by-object-reference" or "pass-by-assignment."
Pass by Object Reference
Immutable Objects: For immutable objects (e.g., integers, strings, tuples), we cannot change the object itself. We can reassign the parameter to a new value, but this does not affect the original object outside the function.
def modify_immutable(x):
x = 10 # Reassigns x to a new integer object
print(x) # Output: 10
a = 5
modify_immutable(a)
print(a) # Output: 5
In the above code, a remains 5 because integers are immutable, and reassigning x inside the function does not affect a.
Mutable Objects: For mutable objects (e.g., lists, dictionaries), we can modify the object itself. Changes to the object inside the function are reflected outside the function.
def modify_mutable(lst):
lst.append(4) # Modifies the list in place
print(lst) # Output: [1, 2, 3, 4]
my_list = [1, 2, 3]
modify_mutable(my_list)
print(my_list) # Output: [1, 2, 3, 4]
In the above code, my_list is modified inside the function because lists are mutable, and changes to lst are reflected in my_list.
Types of Functions
Python has two type of functions:
Built-in Functions: They are provided by Python and are available for use without needing to define them. They are part of Python’s standard library and offer a wide range of functionality for common tasks.
User defined Functions: They are created by the programmer to perform specific tasks that are not covered by built-in functions. We define them using the def keyword.
Built-in functions
abs()
Returns the absolute value of a number
print(abs(-5)) # Output: 5
all()
Returns True if all elements in an iterable are true (or if the iterable is empty).
print(all([True, True, False])) # Output: False
print(all([1, 2, 3])) # Output: True
ascii ()
Returns a string containing a printable representation of an object, but escapes non-ASCII characters.
print(ascii("hello")) # Output: 'hello'
print(ascii("helloñ")) # Output: 'hello\xf1'
bool()
Converts a value to a Boolean (True or False).
print(bool(0)) # Output: False
print(bool("text")) # Output: True
enumerate()
Adds a counter to an iterable and returns it as an enumerate object.
for index, value in enumerate(["a", "b", "c"]):
print(index, value)
# Output:
# 0 a
# 1 b
# 2 c
format()
Formats a value into a string according to a specified format.
print(format(123.456, ".2f")) # Output: 123.46
print(format("text", "^10")) # Output: ' text '
getattr()
Retrieves an attribute from an object, with an optional default value if the attribute does not exist.
class MyClass:
attr = "value"
obj = MyClass()
print(getattr(obj, 'attr')) # Output: 'value'
print(getattr(obj, 'missing', 'default')) # Output: 'default'
id()
Returns the unique identifier for an object (its memory address).
x = 10
print(id(x)) # Output: (unique id for object 10)
len()
Returns the length (number of items) of an object, such as a string, list, or dictionary.
print(len("hello")) # Output: 5
print(len([1, 2, 3])) # Output: 3
map()
Applies a function to all items in an iterable and returns an iterator of results.
def square(x):
return x * x
numbers = [1, 2, 3, 4]
squared = map(square, numbers)
print(list(squared)) # Output: [1, 4, 9, 16]
min()
Returns the smallest item in an iterable or the smallest of two or more arguments.
print(min(1, 2, 3)) # Output: 1
print(min([4, 2, 8, 6])) # Output: 2
pow()
Returns the result of raising the first argument to the power of the second argument (i.e., x**y). Optionally, a third argument can be used for modulus.
print(pow(2, 3)) # Output: 8
print(pow(2, 3, 3)) # Output: 2 (8 % 3)
print()
Outputs text or other data to the console.
print("Hello, World!") # Output: Hello, World!
setattr()
Sets an attribute of an object to a specified value.
class MyClass:
pass
obj = MyClass()
setattr(obj, 'attr', 'value')
print(obj.attr) # Output: 'value'
sorted()
Returns a new sorted list from the elements of an iterable.
print(sorted([3, 1, 2])) # Output: [1, 2, 3]
print(sorted("hello", reverse=True)) # Output: 'ollhe'
type ()
Returns the type of an object.
print(type(123)) # Output: <class 'int'>
print(type("text")) # Output: <class 'str'>
User defined functions
Basic Function
def greet(name):
return f"Hello, {name}!"
print(greet("Alice")) # Output: Hello, Alice!
Function with Default Parameters
def multiply(x, y=2):
return x * y
print(multiply(3)) # Output: 6 (y defaults to 2)
print(multiply(3, 4)) # Output: 12
Function with Multiple Return Values
def divide_and_remainder(x, y):
return x // y, x % y
quotient, remainder = divide_and_remainder(10, 3)
print(quotient) # Output: 3
print(remainder) # Output: 1
Function with Arbitrary Arguments
def print_args(*args):
for arg in args:
print(arg)
print_args(1, 2, 3) # Output: 1 \n 2 \n 3
Function with Keyword Arguments
def describe_person(name, **kwargs):
print(f"Name: {name}")
for key, value in kwargs.items():
print(f"{key}: {value}")
describe_person("Alice", age=30, city="New York")
# Output: Name: Alice \n age: 30 \n city: New York
Recursion
Recursion is a programming technique where a function calls itself to solve a problem. It is a powerful concept for solving problems that can be broken down into smaller, similar subproblems.
A recursive function typically has two main components:
Base Case: The condition under which the recursion stops. It provides an answer without making further recursive calls.
Recursive Case: The part of the function that includes the recursive call. It typically involves breaking down the problem into smaller instances of the same problem.
Syntax:
def recursive_function(parameters):
if base_case_condition:
return base_case_result
else:
# Recursive case
return recursive_function(modified_parameters)
Factorial Using recursion
he factorial of a non-negative integer 𝑛 n is the product of all positive integers less than or equal to 𝑛 n. It is often defined recursively as:
Factorial of 0 or 1: 1
Factorial of n: 𝑛 × factorial of ( 𝑛 − 1 ) n×factorial of (n−1)
def factorial(n):
# Base case
if n == 0:
return 1
else:
# Recursive case
return n * factorial(n - 1)
print(factorial(5)) # Output: 120
The factorial function calculates the factorial of a non-negative integer n using recursion. The function starts with a base case where if n is 0, it returns 1 since the factorial of 0 is 1. For any other positive integer n, the function returns n multiplied by the factorial of n - 1. This recursive call continues until it reaches the base case. For example, calling factorial(5) results in the calculation 5x4x3x2x1, which equals 120.
Anonymous Functions
Anonymous functions are functions that are defined without a name. The most common way to create anonymous functions is by using the lambda keyword. These functions are useful for short-lived operations where defining a full function might be overkill.
Lambda Functions
A lambda function is a small, anonymous function defined using the lambda keyword. Unlike regular functions defined with def, lambda functions are typically used for short, simple operations and are often used as arguments to higher-order functions like map(), filter(), and sorted().
Syntax:
lambda arguments: expression
lambda: The keyword to define an anonymous function.
arguments: A comma-separated list of parameters.
expression: A single expression that the function evaluates and returns
Basic Lambda Function
# Lambda function that adds 10 to the input
add_ten = lambda x: x + 10
print(add_ten(5)) # Output: 15
Lambda with Map()
# Use lambda to double each element in a list
numbers = [1, 2, 3, 4]
doubled = map(lambda x: x * 2, numbers)
print(list(doubled)) # Output: [2, 4, 6, 8]
Lambda with filter()
# Filter out even numbers using lambda
numbers = [1, 2, 3, 4, 5]
even_numbers = filter(lambda x: x % 2 == 0, numbers)
print(list(even_numbers)) # Output: [2, 4]
Lambda with sorted()
# Sort a list of tuples by the second element using lambda
tuples = [(1, 'one'), (3, 'three'), (2, 'two')]
sorted_tuples = sorted(tuples, key=lambda x: x[1])
print(sorted_tuples) # Output: [(1, 'one'), (2, 'two'), (3, 'three')]
Lambda with inline Operations
# Using lambda directly in a function call
result = (lambda x, y: x * y)(5, 3)
print(result) # Output: 15
Modules
Python Modules are individual files that contain Python code. They are the smallest unit of code organization and can include functions, classes, and variables.
A module is a single Python file (.py) that can be imported into other modules or scripts.
The name of the module is the name of the file without the .py extension.
Modules allow us to break down our code into manageable parts, making it easier to reuse and maintain.
Creating a Module
Let's create a file named math_operations.py with the following content
# math_operations.py
def add(x, y):
return x + y
def subtract(x, y):
return x - y
Using a Module
We can import and use this module in another script
# main.py
import math_operations
result = math_operations.add(5, 3)
print(result) # Output: 8
Packages
Python Packages are a way of organizing related modules into a directory hierarchy. A package is essentially a directory that contains multiple modules and a special file called init.py.
A package is a directory that contains multiple module files and an init.py file.
init.py file is executed when the package is imported and can be empty or contain initialization code. It tells Python that the directory should be treated as a package.
Packages can contain sub-packages, allowing for a hierarchical organization of modules.
Creating a Package
Let's create the following directory structure
my_package/
├── __init__.py
├── math_operations.py
└── utils.py
init.py can be empty or contain initialization code.
math_operations.py is defined below
def add(x, y):
return x + y
def subtract(x, y):
return x - y
utils.py is defined below
def greet(name):
return f"Hello, {name}!"
Using a package
We can import and use modules from this package in another script
# main.py
from my_package import math_operations, utils
result = math_operations.add(5, 3)
print(result) # Output: 8
message = utils.greet("Alice")
print(message) # Output: Hello, Alice!
If we want to import everything from math_operations, we can use the following code.
# main.py
from my_package.math_operations import add, subtract
result = add(5, 3)
print(result) # Output: 8
Exception Handling
Exception handling is a mechanism that allows us to manage and respond to runtime errors in a controlled way. Instead of letting our program crash when an error occurs, we can use exception handling to catch these errors and handle them gracefully.
Exception: An error that occurs during the execution of a program, such as dividing by zero or accessing a file that doesn’t exist.
Try Block: The block of code where we write the code that might raise an exception.
Except Block: The block of code that runs if an exception occurs in the try block. It catches the exception and allows us to handle it.
Else Block: (Optional) If no exceptions occur in the try block, the code in the else block is executed.
Finally Block: (Optional) This block of code always runs, regardless of whether an exception occurred or not. It is typically used for cleanup actions.
Syntax:
try:
# Code that may raise an exception
risky_operation()
except ExceptionType as e:
# Code to handle the exception
handle_exception(e)
else:
# Code that runs if no exception occurs
no_exception_occurred()
finally:
# Code that always runs, regardless of exceptions
cleanup()
Basic Exception Handling
try:
result = 10 / 2
except ZeroDivisionError:
print("You can't divide by zero!")
else:
print(f"Result is {result}") # Runs if no exception occurs
finally:
print("This will always be executed.")
The try block contains code that might raise a ZeroDivisionError. The except block catches this specific error and prints a message.
The else block runs if no exceptions are raised in the try block, and the finally block always runs regardless of whether an exception occurred or not.
Built-in Exceptions
Python comes with a set of built-in exceptions that represent various types of errors.
Some of the most common used built-in exceptions are:
Exception: The base class for all built-in exceptions. It is used to catch any exception. We can use this as base class.
ZeroDivisionError: Raised when a division by zero occurs.
Example: x = 1 / 0
ValueError: Raised when a function receives an argument of the right type but inappropriate value.
Example: int("string")
TypeError: Raised when an operation or function is applied to an object of inappropriate type.
Example: "string" + 1
IndexError: Raised when a sequence subscript is out of range.
Example: my_list = [1, 2]; my_list[3]
KeyError: Raised when a dictionary key is not found.
Example: my_dict = {'a': 1}; my_dict['b']
FileNotFoundError: Raised when trying to open a file that does not exist.
Example: open('non_existent_file.txt')
AttributeError: Raised when an invalid attribute reference is made.
Example: 'string'.non_existent_method()
IOError: Raised when an I/O operation fails (e.g., file operations).
Example: This exception is now an alias for OSError.
OSError: Raised for operating system-related errors.
Example: os.remove('non_existent_file.txt')
User-defined Exceptions
We can create our own custom exceptions by subclassing the built-in Exception class or any of its subclasses. This allows us to define exceptions specific to our application's needs.
# Define Exception class
class MyCustomError(Exception):
def __init__(self, message):
self.message = message
super().__init__(self.message)
# Raise custom exception
def do_something(value):
if value < 0:
raise MyCustomError("Value must be non-negative.")
return value * 2
try:
print(do_something(-1))
except MyCustomError as e:
print(e)
MyCustomError class inherits from Exception and takes a custom error message. The do_something function raises MyCustomError if the value is negative. The try block catches this exception and prints the error message.
Output formatting
Output formatting in Python is crucial for presenting data in a clear and readable manner. Python provides several methods to format output, each with its own strengths and use cases.
Using the print() Function
Basic Formatting
print("Hello, World!")
Separator and End Arguments
print("Hello", "World", sep=", ", end="!\n")
sep: Separator between items.
end: String appended after the last item
String Formatting with str.format()
Basic
name = "Alice"
age = 30
print("Name: {}, Age: {}".format(name, age))
Positional and Keyword Arguments
print("{0} is {1} years old.".format(name, age))
print("{name} is {age} years old.".format(name="Bob", age=25))
Formatting with Alignment, Width, and Precision
# Alignment
print("{:<10} | {:>10}".format("left", "right"))
# Width and Precision
pi = 3.141592653589793
print("Pi to 2 decimal places: {:.2f}".format(pi))
f-Strings (Formatted String Literals)
Basic
name = "Alice"
age = 30
print(f"Name: {name}, Age: {age}")
Expression Evaluation
import math
print(f"The square root of 2 is approximately {math.sqrt(2):.2f}")
Alignment and Padding
print(f"{'left':<10} | {'right':>10}")
Formatting for Data Tables
The tabulate library provides a convenient way to format tabular data.
from tabulate import tabulate
data = [["Name", "Age"], ["Alice", 30], ["Bob", 25]]
print(tabulate(data, headers='firstrow', tablefmt='grid'))
JSON Output Formatting
Pretty printing Json Data
import json
data = {"name": "Alice", "age": 30, "city": "New York"}
print(json.dumps(data, indent=4))
Code Writing Style
Coding styles help ensure that code is readable, maintainable, and consistent across different projects and teams.
The most commonly followed coding style guide is PEP 8, which provides conventions for writing Python code.
Indentation: Four spaces per indentation level.
def example_function():
if True:
print("Hello, World!")
Line Length: Limit all lines to 79 characters.
# Good
def some_function_with_a_reasonably_long_name_and_parameters(x, y, z):
return x + y + z
# Bad (exceeds 79 characters)
def some_function_with_a_reasonably_long_name_and_parameters(x, y, z, a, b, c, d, e, f):
return x + y + z + a + b + c + d + e + f
Blank Lines:
Function and Class Definitions*: Separate top-level function and class definitions with two blank lines.*
Method Definitions*: Separate method definitions within a class with one blank line.*
class MyClass:
def __init__(self, value):
self.value = value
def get_value(self):
return self.value
Imports*: Imports should be on separate lines and grouped in three categories: standard library imports, related third-party imports, and local application/library-specific imports.*
import os
import sys
import requests
from mymodule import myfunction
Naming Conventions
Variables*: Use snake_case for variables and functions.*
Classes*: Use CamelCase for class names.*
Constants*: Use UPPER_CASE for constants*
def calculate_area(radius):
PI = 3.14159
return PI * radius * radius
class Circle:
pass
Whitespace: Avoid extra spaces in expressions and statements.
# Good
total = (x + y) * z
# Bad
total = ( x + y ) * z
Comments
Block Comments*: Use block comments to describe sections of code and place them above the code they describe.*
Inline Comments*: Use inline comments sparingly and place them on the same line as the statement they comment on.*
# This function calculates the area of a circle
def calculate_area(radius):
PI = 3.14159 # Define the value of PI
return PI * radius * radius
Docstrings: Write docstrings for all public modules, functions, classes, and methods to describe their purpose and usage. Use triple double quotes (""") for docstrings.
def add(a, b):
"""
Add two numbers and return the result.
Parameters:
a (int): The first number.
b (int): The second number.
Returns:
int: The sum of a and b.
"""
return a + b
Exception Handling: Prefer try-except blocks for handling exceptions and avoid catching generic exceptions unless absolutely necessary.
try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero.")
Function and Variable Annotations: Provide type hints for function arguments and return values.
def multiply(x: int, y: int) -> int:
return x * y
Programming Paradigm
Programming paradigms are approaches or styles of programming that provide different methodologies for solving problems and structuring code. As Python is a multi-paradigm language, it supports several programming paradigms.
Object Oriented Programming
Object Oriented Programming (OOP) is a paradigm based on the concept of "objects," which contain data and code. The data is represented as fields (attributes), and the code is represented as methods (functions).
Features
Classes and Objects: In Python, a class is a blueprint for creating objects. It encapsulates data (attributes) and behavior (methods) into a single entity. Objects are instances of classes.
Inheritance: We can create new classes based on existing ones, inheriting attributes and methods.
Encapsulation: OOP promotes bundling of data and methods into a single unit (class) and controlling access to that data.
Polymorphism: Objects of different classes can be treated as objects of a common superclass. Methods can have the same name but behave differently based on the object’s class.
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError
class Dog(Animal):
def speak(self):
return "Woof!"
dog = Dog("Buddy")
print(dog.speak()) # Output: Woof!
Procedural Programming
Procedural Programming or imperative programming is based on the concept of procedure calls. Programs are structured as sequences of instructions (procedures or functions) that operate on data.
Features:
Functions: In Python, functions are blocks of reusable code that perform a specific task. They help in organizing code, avoiding repetition, and improving readability.
State Management: The state of the program is managed through variables and can be modified by functions.
Control Flow: Control structures such as loops and conditionals manage the execution flow.
def greet(name):
return f"Hello, {name}!"
print(greet("Alice")) # Output: Hello, Alice!
Functional Programming
Functional Programming treats computation as the evaluation of mathematical functions and avoids changing state or mutable data.
Features:
First-Class Functions: Functions can be passed as arguments, returned from other functions, and assigned to variables.
Immutability: Data is not modified after creation; instead, new data is produced.
Higher-Order Functions: Functions that accept other functions as arguments or return functions.
Pure Functions: Functions that do not have side effects and return the same result given the same input.
- By having no side effects, it means that a pure function does not alter any state outside its scope or interact with the outside world. It does not modify global variables, change the value of input parameters, or perform I/O operations.
def add(x, y):
return x + y
def apply_function(f, x, y):
return f(x, y)
print(apply_function(add, 5, 3)) # Output: 8
Structured Programming
Structured programming focuses on breaking down a program into smaller, manageable sections using structured control flow constructs.
Features:
Top-Down Design: Decomposing a problem into smaller sub-problems.
Control Structures: Uses loops and conditionals to manage execution flow.
Modularity: Code is organized into functions or procedures.
def calculate_area(radius):
return 3.14 * radius * radius
def print_area(radius):
area = calculate_area(radius)
print(f"Area: {area}")
print_area(5) # Output: Area: 78.5
Reflective Programming
Reflective programming involves a program's ability to inspect and modify its own structure and behavior at runtime.
Features:
Introspection: Ability to examine the types or properties of objects at runtime.
Meta-Programming: Writing code that manipulates code (e.g., creating or modifying classes).
Dynamic Behavior: Modifying or extending behavior during execution.
class Example:
def __init__(self, value):
self.value = value
example = Example(10)
print(dir(example)) # Output: List of attributes and methods
setattr(example, 'new_attr', 20)
print(example.new_attr) # Output: 20
Declarative Programming
Declarative programming focuses on what should be done rather than how to do it. Declarative programming is less about control flow and more about specifying properties and constraints.
Features:
Expressiveness: Describe what the program should accomplish.
Abstraction: Abstracts away the control flow and implementation details.
Examples: SQL for querying databases, regular expressions for pattern matching.
# Using a list comprehension to declaratively generate a list
squares = [x * x for x in range(10)]
print(squares) # Output: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
After Words
I hope this comprehensive tutorial was helpful to you. Please feel free to reach out in case of any doubts or errors.




