Class Definition
In Python, a class is defined using the class keyword followed by the class name and a colon. The body of the class is indented and may contain attributes and methods.
class Vehicle:
category = "Land"
def __init__(self, brand, model):
self.brand = brand
self.model = model
def describe(self):
print(f"{self.brand} {self.model}")
# Usage
car = Vehicle("Toyota", "Camry")
car.describe() # Output: Toyota Camry
The __init__ method initializes instance attributes. The self parameter refers to the current instance and is used to access its attributes and methods.
Inheritance
A subclass inherits attributes and methods from a parent class. It can override or extend functionality.
class Car(Vehicle):
def __init__(self, brand, model, doors):
super().__init__(brand, model)
self.doors = doors
def describe(self):
super().describe()
print(f"Doors: {self.doors}")
toyota = Car("Toyota", "Camry", 4)
toyota.describe()
# Output:
# Toyota Camry
# Doors: 4
Python supports multiple inheritence, where a class inherits from more than one parent. Method resolution follows the Method Resolution Order (MRO).
Polymorphism
Polymorphism allows objects of different classes to be treated through a common interface. This is achieved via method overriding or duck typing.
class Bird:
def speak(self):
print("Chirp!")
class Robot:
def speak(self):
print("Beep!")
def announce(entity):
entity.speak()
announce(Bird()) # Chirp!
announce(Robot()) # Beep!
Modules and Packages
A module is a .py file containing related code. A package is a directory containing an __init__.py file (optional in Python ≥3.3 but recommended) and one or more modules.
The __init__.py file can:
- Mark the directory as a package.
- Initialize package-level data.
- Control what is imported with
from package import *via the__all__list.
# mypkg/__init__.py
__all__ = ["utils", "core"]
Built-in Data Structures
Lists are ordered, mutable sequences.
items = [10, 20, 30]
items.append(40) # Add element
items.extend([50, 60]) # Add multiple elements
print(items[1:4]) # Slicing: [20, 30, 40]
Sets store unique, unordered elements.
unique_nums = {1, 2, 3}
unique_nums.add(4)
print(3 in unique_nums) # True
print(unique_nums | {5}) # Union: {1, 2, 3, 4, 5}
Dictionaries map keys to values.
config = {"host": "localhost", "port": 8080}
config["debug"] = True
for k, v in config.items():
print(f"{k}: {v}")
Dictionary unpacking uses ** to pass key-value pairs as keyword arguments:
def connect(host, port):
return f"Connecting to {host}:{port}"
params = {"host": "example.com", "port": 443}
print(connect(**params)) # Connecting to example.com:443
List Comprehensions and Generator Expresions
List comprehensions provide a concise way to create lists:
squares = [x**2 for x in range(5) if x % 2 == 0] # [0, 4, 16]
word_lengths = {w: len(w) for w in ["cat", "dog", "elephant"] if len(w) > 3}
# {'elephant': 8}
Generator expressions use parentheses and produce items lazily:
evens = (x for x in range(10) if x % 2 == 0)
print(list(evens)) # [0, 2, 4, 6, 8]
Functional Tools
The map() function applies a function to every item in an iterable:
lengths = list(map(len, ["apple", "fig", "banana"])) # [5, 3, 6]
Lambda functions are anonymous, inline functions:
double = lambda x: x * 2
print(double(5)) # 10
String Formatting
f-strings (formatted string literals) embed expressions inside strings:
name = "Eve"
age = 28
msg = f"{name} is {age} years old." # Eve is 28 years old.
The str.format() method offers an alternative:
msg = "{} is {} years old.".format(name, age)
Dynamic Attribute Access
getattr() retrieves an object’s attribute dynamically:
class Config:
debug = True
cfg = Config()
mode = getattr(cfg, "debug", False) # True
verbosity = getattr(cfg, "log_level", "INFO") # 'INFO'
Logging
The logging module provides a flexible logging system:
import logging
logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
logger = logging.getLogger(__name__)
logger.info("Application started")
Randomness and Reproducibility
Setting a seed ensures reproducible random sequences:
import random
random.seed(123)
print(random.randint(1, 10)) # Always 1
Similarly, NumPy uses np.random.seed().
Slicing
Slicing extracts subsequences with [start:stop:step]:
data = [0, 1, 2, 3, 4, 5]
print(data[1:5:2]) # [1, 3]
print(data[::-1]) # [5, 4, 3, 2, 1, 0]
Ganerators and yield
Functions with yield return generators that produce values on demand:
def count_up_to(n):
i = 1
while i <= n:
yield i
i += 1
for num in count_up_to(3):
print(num) # 1, 2, 3