Skip to main content

Python

  • Link download

  • Checking

    python --version

Basics

Syntax and Variables

Python uses simple, clean syntax. Variables don't need explicit declarations.

# Variable assignment
x = 10
y = 3.14
name = "Alice"
a, b = 5, "Hello"

# Printing variables
print(x, y, name)

Control Flow

x = 10
if x > 5:
print("x is greater than 5")
elif x == 5:
print("x is 5")
else:
print("x is 5 or less")

# Ternary operator
sound = pet == "dog" if "bark" else "meow"

# match-case example (Python 3.10+)
match x:
case 1:
print("x is 1")
case 10:
print("x is 10")
case _: # like default
print("x is something else")
# Loops
for i in range(5): # 0 to 4
print(i)
else: # else block after for (Optional)
print("Loop finished")

for char in "Hello":
print(char)

for fruit in ["apple", "banana", "cherry"]:
print(fruit)

for key, value in {"a": 1, "b": 2}.items():
print(key, value)

# while loop, break, continue
i = 0
while i < 5:
if i == 3:
break
if i == 1:
i += 1
continue
print(i)
i += 1
else:
print("While loop finished")

# pass does nothing, used as a placeholder
for _ in range(3):
pass

Functions

# Function definition
def greet(name = "Guest"):
return f"Hello, {name}!"

# Function call
print(greet()) # Hello, Guest!
print(greet("Alice")) # Hello, Alice!

# Arbitrary arguments
def add(*args):
return sum(args)
print(add(1, 2, 3)) # 6

# Keyword arguments
def multiply(**kwargs):
result = 1
for value in kwargs.values():
result *= value
return result

print(multiply(a=2, b=3, c=4)) # 24

# List comprehension and lambda
squares = [x**2 for x in range(5)]
print(squares) # [0, 2, 4, 6, 8]

# Anonymous function (lambda)
double = lambda x: x * 2

Data Structures

LIST
my_list = [10, 20, 'apple', 3.14, 20]
print(f"Original List: {my_list}") # [10, 20, 'apple', 3.14, 20]

# Accessing elements (indexing)
print(f"Element at position 1: {my_list[1]}") # 20

# Slicing
print(f"Slice from start up to position 3 (exclusive): {my_list[:3]}") # [10, 20, 'apple']

# Modifying an element
my_list[0] = 15
print(f"List after changing the first element: {my_list}") # [15, 20, 'apple', 3.14, 20]

# Adding an element
my_list.append('banana')
print(f"List after appending 'banana': {my_list}") # [15, 20, 'apple', 3.14, 20, 'banana']

# Removing an element (by value)
my_list.remove(20)
print(f"List after removing the first occurrence of 20: {my_list}") # [15, 'apple', 3.14, 20, 'banana']

# Iterating through a List
print("Elements in the List:")
for item in my_list:
print(item)
TUPLE
my_tuple = (1, 'two', 3, 'two', 5)
print(f"Original Tuple: {my_tuple}") # (1, 'two', 3, 'two', 5)

# Accessing elements (indexing)
print(f"Element at position 2: {my_tuple[2]}") # 3

# Slicing
print(f"Slice from position 1 up to 4 (exclusive): {my_tuple[1:4]}") # ('two', 3, 'two')

# **Note:** Cannot add, remove, or change elements after creation

# Counting element occurrences
print(f"Count of 'two': {my_tuple.count('two')}") # 2
SET
my_set = {100, 200, 300, 100, 400}
print(f"Original Set (duplicate 100 removed): {my_set}") # {100, 200, 300, 400}

# Adding an element
my_set.add(500)
print(f"Set after adding 500: {my_set}") # {100, 200, 300, 400, 500}

# Removing an element
my_set.discard(200) # .remove() raises an error if the element is not found
print(f"Set after discarding 200: {my_set}") # {100, 300, 400, 500}

# Basic Set Operations (Union, Intersection, Difference)
set_A = {1, 2, 3, 4}
set_B = {3, 4, 5, 6}
print(f"Union (A U B): {set_A.union(set_B)}") # {1, 2, 3, 4, 5, 6}
print(f"Intersection (A n B): {set_A.intersection(set_B)}") # {3, 4}
print(f"Difference (A - B): {set_A.difference(set_B)}") # {1, 2}
DICTIONARY
my_dict = {
'name': 'Alice',
'age': 30,
'city': 'New York'
}
print(f"Original Dictionary: {my_dict}")
# {'name': 'Alice', 'age': 30, 'city': 'New York'}

# Accessing value by Key
print(f"Name: {my_dict['name']}") # Alice

# Modifying or adding a new Key-Value pair
my_dict['age'] = 31 # Modifying
my_dict['job'] = 'Engineer' # Adding new
print(f"Dictionary after modification and addition: {my_dict}")
# {'name': 'Alice', 'age': 31, 'city': 'New York', 'job': 'Engineer'}

# Deleting a Key-Value pair
del my_dict['city']
print(f"Dictionary after deleting 'city': {my_dict}")
# {'name': 'Alice', 'age': 31, 'job': 'Engineer'}

# Iterating through the Dictionary
print("Key and Value pairs:")
for key, value in my_dict.items():
print(f"{key}: {value}")

# Getting all Keys or Values
print(f"Keys: {my_dict.keys()}") # dict_keys(['name', 'age', 'job'])
print(f"Values: {my_dict.values()}") # dict_values(['Alice', 31, 'Engineer'])

Quick Summary Table

CollectionOrderedMutableDuplicates AllowedPrimary Use Case
ListYesYesYesGeneral purpose ordered sequence of items
TupleYesNoYesImmutable sequence for fixed data (e.g., coordinates)
SetNoYesNoStoring a collection of unique items
DictionaryYes (since Py 3.7)YesNo (Keys must be unique)Storing data mappings as Key: Value pairs

Error Handling

try:
x = int("not a number")
except ValueError as e:
print(f"Error: {e}")
finally: # always executes. Optional
print("Execution completed.")

Object-Oriented Programming (OOP)

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

# Creating instances of the Dog class
dog1 = Dog("Buddy", 9)
dog2 = Dog("Miles", 4)
print(dog1.name) # Output: Buddy
print(dog2.age) # Output: 4
Inheritance
class Animal:
def speak(self):
return "Animal speaks"

class Cat(Animal):
def speak(self):
return "Meow"

cat = Cat()
print(cat.speak()) # Output: Meow
Encapsulation
class BankAccount:
def __init__(self, balance):
self.__balance = balance # Private attribute
def deposit(self, amount):
if amount > 0:
self.__balance += amount
def get_balance(self):
return self.__balance

account = BankAccount(1000)
account.deposit(500)
print(account.get_balance()) # Output: 1500
Polymorphism
class Bird:
def fly(self):
return "Bird is flying"

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

def make_it_fly(flying_object):
print(flying_object.fly())

bird = Bird()
plane = Airplane()
make_it_fly(bird) # Output: Bird is flying
make_it_fly(plane) # Output: Airplane is flying
Abstraction
from abc import ABC, abstractmethod

class Shape(ABC):
@abstractmethod
def area(self):
pass

class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height

rect = Rectangle(5, 10)
print(rect.area()) # Output: 50

Python Modules

A module is simply a Python file (.py) containing functions, classes, or variables that can be reused across different programs.

Example Module: math_utils.py

# math_utils.py
def add(a, b):
return a + b
def multiply(a, b):
return a * b
PI = 3.14159

Using a Module

import math_utils
print(math_utils.add(2, 3)) # Output: 5
print(math_utils.multiply(4, 5)) # Output: 20
print(math_utils.PI) # Output: 3.14159

Python Packages

A package is a collection of modules organized in a directory with a special file __init__.py, which tells Python that the directory should be treated as a package.

Example Package Structure

my_package/

├── __init__.py
├── module1.py
└── module2.py

Importing Modules from a Package

from my_package import module1
from my_package.module2 import some_function
module1.some_function()
some_function()

Key Differences

FeatureModulePackage
DefinitionA single .py fileA directory with multiple modules and __init__.py
UsageReusable code in small unitsGroups related modules logically
StructureFlat and simpleHierarchical and scalable

Best Practices

  • Keep related functions and classes within the same module.
  • Group modules logically into packages for larger projects.
  • Use descriptive and consistent names for modules and packages.
  • Avoid circular imports to maintain clean dependency flows.

Standard Library Modules

Python comes with a rich standard library of modules for various tasks:

  • os: Interact with the operating system
  • sys: Access system-specific parameters and functions
  • math: Mathematical functions
  • datetime: Work with dates and times
  • json: Handle JSON data
  • re: Regular expressions
  • random: Generate random numbers
  • collections: Specialized container datatypes

File I/O

# Writing to a file
with open("test.txt", "w") as file:
file.write("Hello, World!")

# Reading from a file
with open("test.txt", "r") as file:
content = file.read()
print(content)

File Modes

  • "r": Read (default mode, file must exist)
  • "w": Write (creates a new file or truncates existing file)
  • "a": Append (creates a new file or appends to existing file)
  • "b": Binary mode (e.g., "rb" or "wb")
  • "t": Text mode (default mode, e.g., "rt" or "wt")

Working with File Paths

import os
# Get current working directory
cwd = os.getcwd()
print(f"Current Working Directory: {cwd}")

# Join paths
file_path = os.path.join(cwd, "test.txt")
print(f"File Path: {file_path}")

# Check if file exists
exists = os.path.exists(file_path)
print(f"File exists: {exists}")

# Get file size
size = os.path.getsize(file_path)
print(f"File size: {size} bytes")

Handling Large Files

# Reading large files line by line
with open("large_file.txt", "r") as file:
for line in file:
process(line) # Replace with actual processing logic

# Writing large files in chunks
with open("large_output.txt", "w") as file:
for chunk in generate_large_data(): # Replace with actual data generation logic
file.write(chunk)

CSV File Handling

import csv
# Writing to a CSV file
with open("data.csv", "w", newline='') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(["Name", "Age", "City"])
writer.writerow(["Alice", 30, "New York"])
writer.writerow(["Bob", 25, "Los Angeles"])

# Reading from a CSV file
with open("data.csv", "r") as csvfile:
reader = csv.reader(csvfile)
for row in reader:
print(row)

JSON File Handling

import json
# Writing to a JSON file
data = {
"name": "Alice",
"age": 30,
"city": "New York"
}
with open("data.json", "w") as jsonfile:
json.dump(data, jsonfile)

# Reading from a JSON file
with open("data.json", "r") as jsonfile:
data = json.load(jsonfile)
print(data)

Advanced Python Concepts

1. Iterators & Generators

Iterators let you walk through data step-by-step. Generators yield values on demand, keeping things lightweight.

def countdown(n):
while n > 0:
yield n
n -= 1

for num in countdown(3):
print(num)

2. Decorators

Decorators wrap extra behavior around functions without rewriting them.

def log(fn):
def wrapper(*args, **kwargs):
print("Calling:", fn.__name__)
return fn(*args, **kwargs)
return wrapper

@log
def greet():
return "Hello!"

print(greet())

3. Context Managers

They handle setup + cleanup automatically.

class FileManager:
def __init__(self, path):
self.path = path

def __enter__(self):
self.f = open(self.path, "w")
return self.f

def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()

with FileManager("demo.txt") as f:
f.write("Hello world!")

4. Metaclasses

Metaclasses control how classes are created.

class UpperAttrMeta(type):
def __new__(mcls, name, bases, attrs):
uppercase = {
k.upper(): v for k, v in attrs.items() if not k.startswith("__")
}
return super().__new__(mcls, name, bases, uppercase)

class Demo(metaclass=UpperAttrMeta):
value = 10

print(hasattr(Demo, "VALUE")) # True

5. Async Programming

async + await help Python handle I/O-bound tasks smoothly.

import asyncio

async def fetch_data():
await asyncio.sleep(1)
return "done"

async def main():
result = await fetch_data()
print(result)

asyncio.run(main())

6. Descriptors

Descriptors let you customize attribute behavior.

class Positive:
def __set__(self, instance, value):
if value < 0:
raise ValueError("Must be positive")
instance.__dict__["value"] = value

def __get__(self, instance, owner):
return instance.__dict__["value"]

class Account:
value = Positive()

a = Account()
a.value = 50
print(a.value)

7. Type Hints & Static Analysis

Type hints make your intent clearer and help tools catch bugs early.

from typing import List

def average(nums: List[float]) -> float:
return sum(nums) / len(nums)

print(average([1.0, 2.0, 3.0]))

8. Memory Management

Python uses reference counting and garbage collection to manage memory automatically.

import sys
a = []
print(sys.getrefcount(a)) # Reference count
b = a
print(sys.getrefcount(a)) # Increased reference count
del b
print(sys.getrefcount(a)) # Decreased reference count

9. Multi-threading vs Multi-processing

  • Multi-threading: Multiple threads within the same process share memory space. Good for I/O-bound tasks.
  • Multi-processing: Separate memory space for each process. Better for CPU-bound tasks.
import threading
def worker():
print("Worker thread")
thread = threading.Thread(target=worker)
thread.start()
thread.join()
import multiprocessing
def worker():
print("Worker process")
process = multiprocessing.Process(target=worker)
process.start()
process.join()