0% found this document useful (0 votes)
4 views35 pages

python_notes

Python is a high-level, interpreted programming language known for its readability and versatility, supporting various programming paradigms. It has a rich history dating back to the late 1980s and is widely used in web development, data science, AI, and more. Setting up a Python environment involves installing Python and choosing an IDE, while its core features include variables, data types, control flow statements, and built-in data structures like lists.

Uploaded by

sehajsinghx.01
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views35 pages

python_notes

Python is a high-level, interpreted programming language known for its readability and versatility, supporting various programming paradigms. It has a rich history dating back to the late 1980s and is widely used in web development, data science, AI, and more. Setting up a Python environment involves installing Python and choosing an IDE, while its core features include variables, data types, control flow statements, and built-in data structures like lists.

Uploaded by

sehajsinghx.01
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 35

Python Notes

1. Introduction to Python

Python is a high-level, interpreted, general-purpose programming language. Created


by Guido van Rossum and first released in 1991, Python's design philosophy
emphasizes code readability with its notable use of significant indentation. Its
language constructs and object-oriented approach aim to help programmers write
clear, logical code for small and large-scale projects.

Python is dynamically typed and garbage-collected. It supports multiple programming


paradigms, including structured (particularly procedural), object-oriented, and
functional programming. Python is often described as a "batteries included" language
due to its comprehensive standard library.

History and Applications

Python's development began in the late 1980s by Guido van Rossum at Centrum
Wiskunde & Informatica (CWI) in the Netherlands as a successor to the ABC language,
which was inspired by SETL. Van Rossum is Python's principal author, and his
continuing central role in deciding the direction of Python is reflected in the title given
to him by the Python community: Benevolent Dictator For Life (BDFL) – a title he
relinquished on July 12, 2018.

Python 2.0 was released in 2000, introducing features like list comprehensions and a
garbage collection system capable of collecting reference cycles. Python 3.0, released
in 2008, was a major revision of the language that is not entirely backward-compatible,
and much Python 2 code does not run unmodified on Python 3. However, Python 3 has
gained widespread adoption and is the recommended version for new development.

Python is used in a wide variety of applications, including:

Web Development: Frameworks like Django and Flask are popular for building
robust web applications.
Data Science and Machine Learning: Libraries such as NumPy, Pandas, Scikit-
learn, TensorFlow, and PyTorch make Python a dominant language in these
fields.

Artificial Intelligence: Used for developing AI algorithms and applications.

Automation and Scripting: Its simplicity and extensive libraries make it ideal for
automating repetitive tasks and system administration.

Scientific and Numeric Computing: Widely used in academic and research


settings.

Desktop GUI Applications: Libraries like PyQt and Tkinter allow for the creation
of desktop applications.

Education: Python's readability and gentle learning curve make it a popular


choice for teaching programming.

Setting up Python Environment

To start coding in Python, you need to set up your development environment. This
typically involves installing Python and choosing an Integrated Development
Environment (IDE) or a code editor.

Installation

1. Download Python: Visit the official Python website (python.org) and download
the latest stable version for your operating system (Windows, macOS, Linux).

2. Run Installer:
Windows: Run the installer. Make sure to check the box that says "Add
Python X.Y to PATH" during installation. This will allow you to run Python
from the command line.

macOS: Python often comes pre-installed, but it might be an older version.


It's recommended to install the latest version from python.org or using a
package manager like Homebrew ( brew install python ).

Linux: Python is usually pre-installed. You can check your version by typing
python3 --version in the terminal. If you need a newer version, you can
install it via your distribution's package manager (e.g., sudo apt-get
install python3 on Debian/Ubuntu).
3. Verify Installation: Open your terminal or command prompt and type python -
-version (or python3 --version ). You should see the installed Python version.

Integrated Development Environments (IDEs) and Code Editors

While you can write Python code in any text editor, IDEs and specialized code editors
offer features that significantly enhance productivity, such as syntax highlighting, code
completion, debugging tools, and version control integration.

Popular choices include:

PyCharm: A powerful and full-featured IDE specifically designed for Python


development, available in Community (free) and Professional editions. It offers
excellent debugging, testing, and web development support.

VS Code (Visual Studio Code): A lightweight yet powerful code editor developed
by Microsoft. It supports Python development through extensions, offering
features like IntelliSense, debugging, and Git integration. It's highly customizable
and popular among developers for various languages.

Jupyter Notebook: An open-source web application that allows you to create


and share documents that contain live code, equations, visualizations, and
narrative text. It's widely used in data science for interactive computing and data
exploration.

Sublime Text: A sophisticated text editor for code, markup, and prose. It's known
for its speed, sleek user interface, and powerful features, extensible with plugins.

Atom: A free and open-source text editor developed by GitHub. It's highly
customizable and comes with a built-in package manager, making it easy to add
new features.

Choosing an IDE or editor depends on your personal preference and project needs. For
beginners, VS Code or PyCharm Community Edition are excellent starting points due
to their comprehensive features and ease of use.

2. Python Basics

Python's simplicity makes it an excellent language for beginners. Let's dive into the
fundamental building blocks of Python programming.
Variables and Data Types

A variable is a named location used to store data in the memory. It's like a container
that holds values. In Python, you don't need to declare the type of a variable explicitly;
Python infers it based on the value assigned.

# Assigning values to variables


my_integer = 10
my_float = 20.5
my_string = "Hello, Python!"
my_boolean = True

print(my_integer) # Output: 10
print(my_float) # Output: 20.5
print(my_string) # Output: Hello, Python!
print(my_boolean) # Output: True

# Checking the type of a variable


print(type(my_integer)) # Output: <class 'int'>
print(type(my_float)) # Output: <class 'float'>
print(type(my_string)) # Output: <class 'str'>
print(type(my_boolean)) # Output: <class 'bool'>

Python has several built-in data types:

Integers ( int ): Whole numbers, positive or negative, without a decimal point.


E.g., 5 , -100 , 0 .

Floating-point numbers ( float ): Real numbers with a decimal point. E.g.,


3.14 , -0.001 , 2.0 .

Strings ( str ): Sequences of characters enclosed in single ( ' ) or double ( " )


quotes. E.g., 'Python' , "Programming" .

Booleans ( bool ): Represent truth values, either True or False . Used in logical
operations.

Operators

Operators are special symbols that perform operations on one or more operands
(values or variables).

Arithmetic Operators

Used for mathematical calculations:


Operator Description Example Result

+ Addition 10 + 5 15

- Subtraction 10 - 5 5

* Multiplication 10 * 5 50

/ Division (float) 10 / 3 3.33

// Floor Division 10 // 3 3

% Modulus (remainder) 10 % 3 1

** Exponentiation 2 ** 3 8

Comparison (Relational) Operators

Used to compare two values and return a Boolean result ( True or False ):

| Operator | Description | Example | Result | | :------- | :------------------------- | :----------- | :----


- |\n| == | Equal to | 5 == 5 | True | | != | Not equal to | 5 != 10 | True | | > |
Greater than | 10 > 5 | True | | < | Less than | 5 < 10 | True | | >= | Greater than or
equal to | 10 >= 10 | True | | <= | Less than or equal to | 5 <= 10 | True |

Logical Operators

Used to combine conditional statements:

Operator Description Example Result

True and
and Returns True if both statements are true False
False

or Returns True if one of the statements is true True or False True

Reverses the result, returns False if the result is


not not True False
true

Assignment Operators

Used to assign values to variables:


Operator Example Equivalent to

= x = 5 x = 5

+= x += 3 x = x + 3

-= x -= 3 x = x - 3

*= x *= 3 x = x * 3

/= x /= 3 x = x / 3

//= x //= 3 x = x // 3

%= x %= 3 x = x % 3

**= x **= 3 x = x ** 3

Input and Output

Python provides built-in functions for taking input from the user and displaying
output.

print() : Used to display output to the console.

input() : Used to get input from the user. The input is always returned as a
string.

# Output using print()


print("Hello, World!")
print("My name is", "Manus AI")

# Input using input()


name = input("Enter your name: ")
print("Hello,", name)

# Example of type conversion after input


age_str = input("Enter your age: ")
age_int = int(age_str) # Convert string to integer
print("Next year, you will be", age_int + 1, "years old.")

Type Conversion (Type Casting)

Sometimes you need to convert values from one data type to another. Python provides
built-in functions for this:
int() : Converts to an integer.

float() : Converts to a floating-point number.

str() : Converts to a string.

bool() : Converts to a boolean.

num_str = "123"
num_int = int(num_str)
print(num_int, type(num_int)) # Output: 123 <class 'int'>

num_float = 3.14
num_int_from_float = int(num_float)
print(num_int_from_float, type(num_int_from_float)) # Output: 3 <class 'int'>
(truncates decimal)

int_val = 100
str_val = str(int_val)
print(str_val, type(str_val)) # Output: 100 <class 'str'>

# Non-empty strings and non-zero numbers convert to True


bool_from_str = bool("hello")
bool_from_int = bool(0)
print(bool_from_str, type(bool_from_str)) # Output: True <class 'bool'>
print(bool_from_int, type(bool_from_int)) # Output: False <class 'bool'>

3. Control Flow

Control flow statements are used to execute specific blocks of code based on certain
conditions or to repeat code multiple times. This allows programs to make decisions
and perform actions dynamically.

Conditional Statements (if, elif, else)

Conditional statements allow your program to execute different code blocks based on
whether a condition is true or false. Python uses if , elif (else if), and else
keywords for this purpose.
# if statement
x = 10
if x > 5:
print("x is greater than 5")

# if-else statement
y = 3
if y % 2 == 0:
print("y is an even number")
else:
print("y is an odd number")

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

Key points for conditional statements: * Python uses indentation (whitespace) to


define code blocks. This is crucial and different from many other languages that use
curly braces {} . * Conditions are evaluated to a Boolean ( True or False ) value. *
Only one block among if , elif , else will be executed.

Loops (for, while)

Loops are used to execute a block of code repeatedly. Python provides for and
while loops.

for Loop

The for loop is used for iterating over a sequence (that is, a list, tuple, dictionary, set,
or string).
# Iterating over a list
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)

# Iterating over a string


for char in "Python":
print(char)

# Using range() function


# range(stop): generates numbers from 0 up to (but not including) stop
for i in range(5):
print(i) # Output: 0, 1, 2, 3, 4

# range(start, stop): generates numbers from start up to (but not including)


stop
for i in range(2, 7):
print(i) # Output: 2, 3, 4, 5, 6

# range(start, stop, step): generates numbers from start up to stop,


incrementing by step
for i in range(0, 10, 2):
print(i) # Output: 0, 2, 4, 6, 8

while Loop

The while loop executes a block of code as long as a condition is true.

# Basic while loop


count = 0
while count < 5:
print(count)
count += 1 # Increment count to avoid infinite loop

# while loop with user input


password = ""
while password != "secret":
password = input("Enter the password: ")
print("Access granted!")

Key points for loops: * Ensure the loop condition eventually becomes false to avoid
infinite loops. * Indentation defines the loop body.

Break, Continue, Pass Statements

These statements are used to alter the normal flow of loops.


break Statement

The break statement terminates the loop entirely and transfers control to the
statement immediately following the loop.

for i in range(10):
if i == 5:
break # Exit the loop when i is 5
print(i) # Output: 0, 1, 2, 3, 4

continue Statement

The continue statement skips the rest of the current iteration of the loop and
proceeds to the next iteration.

for i in range(5):
if i == 2:
continue # Skip printing when i is 2
print(i) # Output: 0, 1, 3, 4

pass Statement

The pass statement is a null operation; nothing happens when it executes. It is used
as a placeholder when a statement is syntactically required but you don't want any
code to execute.

# Example: Function or class definition that will be implemented later


def my_function():
pass # TODO: Implement this function later

# Example: Placeholder in a conditional block


num = 10
if num > 5:
pass # Do nothing for now
else:
print("Number is 5 or less")

4. Data Structures

Data structures are fundamental ways to organize and store data in a computer so that
it can be accessed and modified efficiently. Python offers several built-in data
structures that are highly versatile and widely used.
Lists

A list is a mutable (changeable), ordered sequence of elements. Elements in a list are


enclosed in square brackets [] and separated by commas. Lists can contain elements
of different data types.

Creation and Access

# Creating a list
my_list = [1, 2, 3, "apple", "banana", True]
print(my_list) # Output: [1, 2, 3, 'apple', 'banana', True]

# Accessing elements (indexing starts from 0)


print(my_list[0]) # Output: 1
print(my_list[3]) # Output: apple
print(my_list[-1]) # Output: True (negative indexing: last element)

# Slicing (get a sub-list)


print(my_list[1:4]) # Output: [2, 3, 'apple'] (elements from index 1 up to, but
not including, 4)
print(my_list[:3]) # Output: [1, 2, 3] (from beginning to index 3)
print(my_list[3:]) # Output: ['apple', 'banana', True] (from index 3 to end)

Modification

Since lists are mutable, you can change their elements after creation.
my_list = [10, 20, 30, 40]
my_list[0] = 15 # Change element at index 0
print(my_list) # Output: [15, 20, 30, 40]

# Add elements
my_list.append(50) # Add to the end
print(my_list) # Output: [15, 20, 30, 40, 50]

my_list.insert(1, 25) # Insert at a specific index


print(my_list) # Output: [15, 25, 20, 30, 40, 50]

# Remove elements
my_list.remove(20) # Remove first occurrence of value 20
print(my_list) # Output: [15, 25, 30, 40, 50]

my_list.pop() # Remove and return the last element


print(my_list) # Output: [15, 25, 30, 40]

my_list.pop(1) # Remove and return element at index 1


print(my_list) # Output: [15, 30, 40]

del my_list[0] # Delete element at index 0


print(my_list) # Output: [30, 40]

# Other useful list methods


list_a = [1, 2, 3]
list_b = [4, 5, 6]
list_a.extend(list_b) # Add elements of list_b to list_a
print(list_a) # Output: [1, 2, 3, 4, 5, 6]

numbers = [5, 1, 4, 2, 8]
numbers.sort() # Sort the list in ascending order
print(numbers) # Output: [1, 2, 4, 5, 8]

numbers.reverse() # Reverse the order of elements


print(numbers) # Output: [8, 5, 4, 2, 1]

print(len(numbers)) # Output: 5 (length of the list)

Tuples

A tuple is an immutable (unchangeable), ordered sequence of elements. Tuples are


defined by enclosing elements in parentheses () and separating them with commas.
Once a tuple is created, you cannot change its elements.
Creation and Access

# Creating a tuple
my_tuple = (1, 2, "hello", 3.14)
print(my_tuple) # Output: (1, 2, 'hello', 3.14)

# Accessing elements (same as lists)


print(my_tuple[0]) # Output: 1
print(my_tuple[2]) # Output: hello

# Slicing (same as lists)


print(my_tuple[1:3]) # Output: (2, 'hello')

Immutability

Attempting to modify a tuple will result in an error:

# my_tuple[0] = 10 # This will raise a TypeError

Tuples are often used for heterogeneous (different types) data, where the sequence of
elements is important, and for data that should not be changed.

Dictionaries

A dictionary is an unordered collection of key-value pairs. Each key must be unique,


and it maps to a value. Dictionaries are mutable and are defined by enclosing key-
value pairs in curly braces {} .

Creation and Access

# Creating a dictionary
my_dict = {"name": "Alice", "age": 30, "city": "New York"}
print(my_dict) # Output: {'name': 'Alice', 'age': 30, 'city': 'New York'}

# Accessing values using keys


print(my_dict["name"]) # Output: Alice
print(my_dict.get("age")) # Output: 30 (safer way to access, returns None if
key not found)

# Adding new key-value pairs


my_dict["email"] = "alice@example.com"
print(my_dict) # Output: {'name': 'Alice', 'age': 30, 'city': 'New York',
'email': 'alice@example.com'}
Modification

# Changing a value
my_dict["age"] = 31
print(my_dict) # Output: {'name': 'Alice', 'age': 31, 'city': 'New York',
'email': 'alice@example.com'}

# Removing key-value pairs


my_dict.pop("city") # Remove by key and return its value
print(my_dict) # Output: {'name': 'Alice', 'age': 31, 'email':
'alice@example.com'}

del my_dict["age"] # Delete by key


print(my_dict) # Output: {'name': 'Alice', 'email': 'alice@example.com'}

# Other useful dictionary methods


print(my_dict.keys()) # Output: dict_keys(['name', 'email'])
print(my_dict.values()) # Output: dict_values(['Alice', 'alice@example.com'])
print(my_dict.items()) # Output: dict_items([('name', 'Alice'), ('email',
'alice@example.com')])

for key, value in my_dict.items():


print(f"{key}: {value}")
# Output:
# name: Alice
# email: alice@example.com

Sets

A set is an unordered collection of unique elements. Sets are mutable and are defined
by enclosing elements in curly braces {} (similar to dictionaries, but without key-
value pairs) or by using the set() constructor.
Creation and Operations

# Creating a set
my_set = {1, 2, 3, 2, 1} # Duplicate elements are automatically removed
print(my_set) # Output: {1, 2, 3}

# Creating an empty set (important: {} creates an empty dictionary)


empty_set = set()
print(empty_set) # Output: set()

# Adding elements
my_set.add(4)
print(my_set) # Output: {1, 2, 3, 4}

# Removing elements
my_set.remove(2) # Raises KeyError if element not found
print(my_set) # Output: {1, 3, 4}

my_set.discard(1) # Does not raise error if element not found


print(my_set) # Output: {3, 4}

# Set operations
set_a = {1, 2, 3, 4}
set_b = {3, 4, 5, 6}

print(set_a.union(set_b)) # Output: {1, 2, 3, 4, 5, 6} (all unique elements)


print(set_a.intersection(set_b)) # Output: {3, 4} (common elements)
print(set_a.difference(set_b)) # Output: {1, 2} (elements in A but not in B)
print(set_b.difference(set_a)) # Output: {5, 6} (elements in B but not in A)
print(set_a.symmetric_difference(set_b)) # Output: {1, 2, 5, 6} (elements in
either A or B, but not both)

Sets are particularly useful for membership testing, removing duplicates from a
sequence, and performing mathematical set operations.

5. Functions

A function is a block of organized, reusable code that is used to perform a single,


related action. Functions provide better modularity for your application and a high
degree of code reusing.

Defining and Calling Functions

In Python, functions are defined using the def keyword, followed by the function
name, parentheses () , and a colon : . The code block within the function must be
indented.
# Defining a simple function
def greet():
print("Hello, world!")

# Calling the function


greet() # Output: Hello, world!

# Function with parameters


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

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


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

Parameters and Arguments

Parameters: The names listed inside the function definition parentheses.

Arguments: The actual values passed to the function when it is called.

def add_numbers(a, b): # a and b are parameters


result = a + b
print(f"The sum is: {result}")

add_numbers(5, 3) # 5 and 3 are arguments


add_numbers(10, 20)

Default Parameters

You can provide default values for parameters. If an argument is not provided for such
a parameter, its default value is used.

def greet_default(name="Guest"):
print(f"Hello, {name}!")

greet_default() # Output: Hello, Guest!


greet_default("Charlie") # Output: Hello, Charlie!

Keyword Arguments

You can pass arguments using key = value syntax. This allows you to pass
arguments in any order.
def describe_person(name, age):
print(f"{name} is {age} years old.")

describe_person(name="David", age=25)
describe_person(age=30, name="Eve") # Order doesn't matter with keyword
arguments

Arbitrary Arguments ( *args and **kwargs )

*args (Non-keyword Arguments): Allows a function to accept an arbitrary


number of non-keyword arguments. These arguments will be wrapped in a tuple.

**kwargs (Keyword Arguments): Allows a function to accept an arbitrary


number of keyword arguments. These arguments will be wrapped in a dictionary.

def sum_all(*numbers):
total = 0
for num in numbers:
total += num
return total

print(sum_all(1, 2, 3)) # Output: 6


print(sum_all(10, 20, 30, 40)) # Output: 100

def print_info(**data):
for key, value in data.items():
print(f"{key}: {value}")

print_info(name="Frank", age=40, city="London")


# Output:
# name: Frank
# age: 40
# city: London

Return Statement

The return statement is used to exit a function and return a value (or values) to the
caller. If no return statement is present, the function implicitly returns None .

def multiply(a, b):


return a * b

result = multiply(4, 5)
print(result) # Output: 20

def get_full_name(first, last):


return f"{first} {last}"

full_name = get_full_name("Grace", "Hopper")


print(full_name) # Output: Grace Hopper
Scope of Variables

The scope of a variable refers to the region of the code where the variable is accessible.

Local Scope: Variables defined inside a function are local to that function and
cannot be accessed from outside.

Global Scope: Variables defined outside any function are global and can be
accessed from anywhere in the program.

global_var = "I am global"

def my_function():
local_var = "I am local"
print(global_var) # Can access global variable
print(local_var) # Can access local variable

my_function()
print(global_var) # Output: I am global
# print(local_var) # This would raise a NameError

# Using 'global' keyword to modify a global variable inside a function


x = 10
def modify_global():
global x
x = 20
print(f"Inside function, x: {x}")

modify_global()
print(f"Outside function, x: {x}") # Output: Outside function, x: 20

Lambda Functions

Lambda functions (also called anonymous functions) are small, single-expression


functions that are not formally defined using the def keyword. They are created using
the lambda keyword.

# Syntax: lambda arguments: expression

# A simple lambda function


add = lambda a, b: a + b
print(add(2, 3)) # Output: 5

# Lambda function used with built-in functions like filter()


numbers = [1, 2, 3, 4, 5, 6]
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers) # Output: [2, 4, 6]

# Lambda function used with built-in functions like map()


squared_numbers = list(map(lambda x: x * x, numbers))
print(squared_numbers) # Output: [1, 4, 9, 16, 25, 36]
Lambda functions are typically used for short, throwaway functions that are needed
for a brief period, often as arguments to higher-order functions.

6. Modules and Packages

As your Python programs grow larger, it becomes impractical to keep all the code in a
single file. Python provides a way to organize code into reusable units called modules
and packages.

Importing Modules

A module is simply a Python file ( .py ) containing Python definitions and statements.
Modules allow you to logically organize your Python code. When you import a module,
you can use the functions, classes, and variables defined within it.

# Example: math_operations.py
# def add(a, b):
# return a + b
#
# def subtract(a, b):
# return a - b

To use the functions from math_operations.py in another file:


# Method 1: Import the entire module
import math_operations

result_add = math_operations.add(10, 5)
print(result_add) # Output: 15

result_subtract = math_operations.subtract(10, 5)
print(result_subtract) # Output: 5

# Method 2: Import specific functions from a module


from math_operations import add, subtract

result_add = add(20, 10)


print(result_add) # Output: 30

# Method 3: Import all names from a module (generally discouraged for clarity)
from math_operations import *

result_add = add(30, 15)


print(result_add) # Output: 45

# Method 4: Import with an alias


import math_operations as mo

result_add = mo.add(50, 25)


print(result_add) # Output: 75

Python has a rich standard library with many built-in modules that you can import,
such as math , random , datetime , os , sys , etc.

import math
print(math.sqrt(16)) # Output: 4.0

import random
print(random.randint(1, 10)) # Output: a random integer between 1 and 10

from datetime import datetime


now = datetime.now()
print(now) # Output: current date and time

Creating Custom Modules

To create your own module, simply save your Python code in a file with a .py
extension. For example, if you save the add and subtract functions in a file named
my_module.py , you can then import my_module in other Python scripts located in the
same directory or in a directory included in Python's path.
Understanding Packages

A package is a way of organizing related modules into a single directory hierarchy. A


package is essentially a directory containing a special file called __init__.py (which
can be empty) and other module files or sub-packages.

Consider the following directory structure:

my_project/
├── main.py
└── my_package/
├── __init__.py
├── module_a.py
└── sub_package/
├── __init__.py
└── module_b.py

my_package is a package because it contains __init__.py .

sub_package is a sub-package because it also contains __init__.py .

To import modules from a package:

# In main.py

# Import module_a from my_package


from my_package import module_a

# Assuming module_a.py has a function called 'function_a'


# module_a.function_a()

# Import module_b from sub_package within my_package


from my_package.sub_package import module_b

# Assuming module_b.py has a function called 'function_b'


# module_b.function_b()

# You can also import specific functions directly


from my_package.module_a import function_a
# function_a()

Packages help in avoiding naming conflicts and provide a clear structure for larger
projects. The __init__.py file can also contain initialization code for the package,
though it's often left empty for simple packages.
7. File Handling

File handling is an essential part of any programming language, allowing you to


interact with files stored on your computer. Python provides built-in functions and
methods to perform operations like creating, reading, writing, and appending to files.

Opening and Closing Files

Before you can perform any operations on a file, you need to open it. The open()
function is used for this purpose. It returns a file object, which has methods for
reading, writing, and other file operations.

# Syntax: open(filename, mode)

# Modes:
# "r": Read - Default mode. Opens a file for reading. Error if the file does
not exist.
# "w": Write - Opens a file for writing. Creates the file if it does not exist,
or truncates (empties) the file if it exists.
# "a": Append - Opens a file for appending. Creates the file if it does not
exist. If the file exists, new content is added to the end.
# "x": Create - Creates the specified file. Returns an error if the file
exists.
# "t": Text - Default mode. Opens in text mode.
# "b": Binary - Opens in binary mode (e.g., for images, executables).

# Opening a file for writing (creates or overwrites)


file = open("my_file.txt", "w")
file.write("Hello, this is a test file.\n")
file.write("This is the second line.\n")
file.close() # Always close the file after you are done with it

# Opening a file for reading


file = open("my_file.txt", "r")
content = file.read()
print(content)
file.close()

# Using the `with` statement (recommended)


# The `with` statement ensures that the file is properly closed after its block
finishes,
# even if errors occur.
with open("another_file.txt", "w") as file:
file.write("This file was created using the with statement.\n")
file.write("It closes automatically.\n")

with open("another_file.txt", "r") as file:


content = file.read()
print(content)
Reading from Files

Once a file is opened in read mode, you can use various methods to read its content:

read(size) : Reads at most size bytes (or characters in text mode). If size is
omitted or negative, the entire content of the file is read.

readline() : Reads one entire line from the file.

readlines() : Reads all lines from the file and returns them as a list of strings.

with open("my_file.txt", "r") as file:


print("--- Reading entire file ---")
content = file.read()
print(content)

with open("my_file.txt", "r") as file:


print("--- Reading line by line ---")
line1 = file.readline()
line2 = file.readline()
print(line1, end="") # end="" prevents extra newline
print(line2, end="")

with open("my_file.txt", "r") as file:


print("--- Reading all lines into a list ---")
lines = file.readlines()
for line in lines:
print(line, end="")

Writing to Files

When a file is opened in write ( "w" ) or append ( "a" ) mode, you can use the write()
method to add content.

write(string) : Writes the given string to the file. It does not add a newline
character automatically.

# Writing (overwrites existing content)


with open("write_example.txt", "w") as file:
file.write("First line.\n")
file.write("Second line.\n")

# Appending (adds to the end of existing content)


with open("write_example.txt", "a") as file:
file.write("Third line (appended).\n")

with open("write_example.txt", "r") as file:


print("--- Content after writing and appending ---")
print(file.read())
Error Handling with Files

File operations can often lead to errors (e.g., file not found, permission denied). It's
good practice to handle these errors using try-except blocks.

try:
with open("non_existent_file.txt", "r") as file:
content = file.read()
print(content)
except FileNotFoundError:
print("Error: The file was not found.")
except Exception as e:
print(f"An unexpected error occurred: {e}")

print("Program continues after error handling.")

8. Object-Oriented Programming (OOP)

Object-Oriented Programming (OOP) is a programming paradigm that uses "objects"


to design applications and computer programs. These objects are instances of
"classes," which serve as blueprints. OOP aims to increase the flexibility and
maintainability of programs.

Classes and Objects

Class: A blueprint or a template for creating objects. It defines a set of attributes


(data) and methods (functions) that the created objects will have.

Object: An instance of a class. When a class is defined, no memory is allocated


until an object (instance) is created.
# Defining a class
class Dog:
# Class attribute
species = "Canis familiaris"

# Initializer / Constructor method


def __init__(self, name, age):
self.name = name # Instance attribute
self.age = age # Instance attribute

# Instance method
def bark(self):
return f"{self.name} says Woof!"

def description(self):
return f"{self.name} is {self.age} years old."

# Creating objects (instances of the Dog class)


my_dog = Dog("Buddy", 3)
your_dog = Dog("Lucy", 5)

# Accessing attributes
print(my_dog.name) # Output: Buddy
print(your_dog.age) # Output: 5
print(my_dog.species) # Output: Canis familiaris (class attribute)

# Calling methods
print(my_dog.bark()) # Output: Buddy says Woof!
print(your_dog.description()) # Output: Lucy is 5 years old.

Attributes and Methods

Attributes: Variables associated with a class or an object. They store data.


Class Attributes: Belong to the class itself and are shared by all instances of
the class.

Instance Attributes: Belong to a specific instance of the class and are


unique to that instance.

Methods: Functions defined inside a class that perform actions on the object's
data.
self parameter: The first parameter of any instance method, it refers to
the instance of the class itself. It allows methods to access and manipulate
the instance's attributes.

Inheritance

Inheritance is a mechanism that allows a new class (subclass or derived class) to


inherit attributes and methods from an existing class (superclass or base class). This
promotes code reusability and establishes a natural hierarchy.

class Animal:
def __init__(self, name):
self.name = name

def speak(self):
raise NotImplementedError("Subclass must implement abstract method")

class Cat(Animal):
def __init__(self, name, breed):
super().__init__(name) # Call the constructor of the base class
self.breed = breed

def speak(self):
return f"{self.name} says Meow!"

def get_breed(self):
return f"{self.name} is a {self.breed}."

my_cat = Cat("Whiskers", "Siamese")


print(my_cat.name) # Inherited attribute
print(my_cat.speak()) # Overridden method
print(my_cat.get_breed()) # New method in subclass

# Check if an object is an instance of a class


print(isinstance(my_cat, Cat)) # Output: True
print(isinstance(my_cat, Animal)) # Output: True

Polymorphism

Polymorphism means "many forms." In OOP, it refers to the ability of different objects
to respond to the same method call in their own way. This is often achieved through
method overriding (as seen in inheritance) or by having functions that can operate on
objects of different types.

class Bird:
def fly(self):
print("Bird is flying")

class Airplane:
def fly(self):
print("Airplane is flying")

def make_it_fly(obj):
obj.fly()

bird = Bird()
airplane = Airplane()

make_it_fly(bird) # Output: Bird is flying


make_it_fly(airplane) # Output: Airplane is flying
Encapsulation

Encapsulation is the bundling of data (attributes) and methods that operate on the
data within a single unit (class). It also involves restricting direct access to some of an
object's components, which can prevent accidental modification of data. In Python,
encapsulation is achieved through conventions (prefixing with underscores) rather
than strict access modifiers.

Public attributes/methods: Accessible from anywhere. (e.g., self.name )

Protected attributes/methods: Indicated by a single leading underscore (e.g.,


_protected_attribute ). By convention, these should not be accessed directly
from outside the class, but it's not enforced.

Private attributes/methods: Indicated by two leading underscores (e.g.,


__private_attribute ). Python internally mangles these names to make them
harder to access directly, providing a stronger form of encapsulation.

class BankAccount:
def __init__(self, balance):
self.__balance = balance # Private attribute

def deposit(self, amount):


if amount > 0:
self.__balance += amount
print(f"Deposited {amount}. New balance: {self.__balance}")
else:
print("Deposit amount must be positive.")

def withdraw(self, amount):


if 0 < amount <= self.__balance:
self.__balance -= amount
print(f"Withdrew {amount}. New balance: {self.__balance}")
else:
print("Invalid withdrawal amount or insufficient funds.")

def get_balance(self):
return self.__balance

account = BankAccount(1000)
account.deposit(500)
account.withdraw(200)
# print(account.__balance) # This would cause an AttributeError
print(account.get_balance()) # Access balance through a public method
9. Error and Exception Handling

Errors are inevitable in programming. Python provides mechanisms to handle errors


gracefully, preventing your program from crashing and allowing you to manage
unexpected situations. These mechanisms are primarily try , except , else , and
finally blocks.

Types of Errors

Understanding different types of errors helps in debugging and handling them


effectively:

Syntax Errors (Parsing Errors): These occur when the parser detects an
incorrect statement. They prevent the program from running. python # Example
of a syntax error # if x > 5 # print("Hello") # Missing colon after 5

Runtime Errors (Exceptions): These occur during the execution of the program.
Even if a statement is syntactically correct, it may cause an error during
execution. When a runtime error occurs, Python raises an "exception." ```python
# Example of a runtime error (ZeroDivisionError) # result = 10 / 0
Example of a runtime error
(NameError)

print(undefined_variable)

Example of a runtime error


(TypeError)

len(123)
* **Logical Errors:** These are the hardest to find. They occur when
the program runs without crashing, but produces incorrect or
unexpected results because of a flaw in the program's logic. python
Example of a logical error

average = sum_of_numbers /
count_of_numbers

If count_of_numbers is sometimes 0,
it leads to ZeroDivisionError (runtime
error)

If sum_of_numbers or
count_of_numbers are calculated
incorrectly, it leads to wrong average
(logical error)
```

try , except , else , finally Blocks

Python uses the try and except statements to handle errors. The code that might
raise an exception is placed inside the try block. If an exception occurs, the code
inside the except block is executed.
# Basic try-except block
try:
num1 = int(input("Enter a number: "))
num2 = int(input("Enter another number: "))
result = num1 / num2
print(f"The result is: {result}")
except ZeroDivisionError:
print("Error: Cannot divide by zero!")
except ValueError:
print("Error: Invalid input. Please enter a valid number.")
except Exception as e: # Catch any other unexpected errors
print(f"An unexpected error occurred: {e}")

print("Program continues after error handling.")

try block: The code that is to be monitored for exceptions.

except block: This block is executed if an exception occurs in the try block.
You can specify the type of exception to catch (e.g., ZeroDivisionError ,
ValueError ). You can have multiple except blocks to handle different types of
exceptions.

else block: (Optional) This block is executed if no exception occurs in the try
block.

try:
x = 10
y = 2
result = x / y
except ZeroDivisionError:
print("Division by zero!")
else:
print(f"Division successful. Result: {result}") # Executed only if no
exception

finally block: (Optional) This block is always executed, regardless of whether


an exception occurred or not. It's typically used for cleanup operations (e.g.,
closing files, releasing resources).

file = None
try:
file = open("data.txt", "r")
content = file.read()
print(content)
except FileNotFoundError:
print("File not found.")
finally:
if file:
file.close()
print("File closed.")
Raising Exceptions

You can also raise your own exceptions in Python using the raise keyword. This is
useful when you want to enforce certain conditions or signal an error in your custom
functions or classes.

def validate_age(age):
if not isinstance(age, (int, float)):
raise TypeError("Age must be a number.")
if age < 0:
raise ValueError("Age cannot be negative.")
print(f"Valid age: {age}")

try:
validate_age(25)
validate_age(-5) # This will raise a ValueError
validate_age("abc") # This will raise a TypeError
except (ValueError, TypeError) as e:
print(f"Validation Error: {e}")

Raising custom exceptions allows you to create more robust and predictable code by
explicitly handling situations that are considered erroneous within your application
logic.

10. Advanced Topics (Briefly)

Python offers many advanced features and concepts that can significantly enhance
your programming capabilities. Here, we briefly touch upon a few of them.

List Comprehensions

List comprehensions provide a concise way to create lists. It consists of brackets


containing an expression followed by a for clause, then zero or more for or if
clauses. The result will be a new list resulting from evaluating the expression in the
context of the for and if clauses which follow it.
# Basic list comprehension
squares = [x**2 for x in range(10)]
print(squares) # Output: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# List comprehension with a condition


even_squares = [x**2 for x in range(10) if x % 2 == 0]
print(even_squares) # Output: [0, 4, 16, 36, 64]

# Nested list comprehension


matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened_list = [num for row in matrix for num in row]
print(flattened_list) # Output: [1, 2, 3, 4, 5, 6, 7, 8, 9]

List comprehensions are generally more readable and often faster than traditional
for loops for creating lists.

Generators

Generators are a simple and powerful tool for creating iterators. They are written like
regular functions but use the yield statement instead of return . When a generator
function is called, it returns an iterator object without starting execution immediately.
When the next() method is called on the iterator, the function executes until it hits a
yield statement. The value provided by yield is returned to the caller. Execution is
paused until next() is called again.

Generators are memory-efficient because they produce items one at a time, only when
requested, rather than creating all items in memory at once.

def countdown(num):
print("Starting countdown")
while num > 0:
yield num
num -= 1

# Create a generator object


gen = countdown(5)

print(next(gen)) # Output: Starting countdown, 5


print(next(gen)) # Output: 4
print(next(gen)) # Output: 3

for x in countdown(3):
print(x)
# Output:
# Starting countdown
# 3
# 2
# 1
Decorators

Decorators are a powerful and unique feature in Python that allow you to modify or
enhance the functionality of functions or methods without changing their source code.
They are essentially functions that take another function as an argument, add some
functionality, and return the modified function.

def my_decorator(func):
def wrapper(*args, **kwargs):
print("Something is happening before the function is called.")
func(*args, **kwargs)
print("Something is happening after the function is called.")
return wrapper

@my_decorator
def say_hello(name):
print(f"Hello, {name}!")

say_hello("World")
# Output:
# Something is happening before the function is called.
# Hello, World!
# Something is happening after the function is called.

Decorators are widely used in web frameworks (e.g., Flask, Django for routing),
logging, authentication, and performance measurement.

Working with External Libraries (Brief Mention)

Python's strength lies significantly in its vast ecosystem of external libraries and
frameworks, which extend its capabilities for almost any task. These libraries are
typically installed using pip (Python's package installer).

NumPy: Fundamental package for numerical computing, providing support for


large, multi-dimensional arrays and matrices, along with a collection of
mathematical functions to operate on these arrays.

Pandas: A powerful data manipulation and analysis library, offering data


structures like DataFrames that are highly efficient for handling structured data.

Matplotlib/Seaborn: Libraries for creating static, interactive, and animated


visualizations in Python.

Scikit-learn: A comprehensive library for machine learning, offering various


classification, regression, clustering, and dimensionality reduction algorithms.

Requests: An elegant and simple HTTP library for making web requests.
To install a library, you would typically use a command like: pip install numpy
pandas

These advanced topics and external libraries are just a glimpse into the extensive
capabilities of Python, allowing developers to build complex and powerful
applications across various domains.

You might also like