---
title: "Python"
description: "Python is an interpreted, high-level programming language known for its readability and simplicity. It supports multiple programming paradigms including procedural, object-oriented, and functional programming."
author: "Mohammad Abu Mattar"
canonical: https://mkabumattar.com/cheatsheets/python
---

# Python

Python is an interpreted, high-level programming language known for its readability and simplicity. It supports multiple programming paradigms including procedural, object-oriented, and functional programming.

Browse the sections below to explore Python syntax, functions, classes, and common patterns.

## Getting Started

Fundamental Python concepts and syntax for beginners.

### Basic Syntax

Python fundamentals including print, variables, comments, and indentation.

**Keywords:** print, variables, comments, indentation, syntax

#### Print statement and variables

```python
name = "Python"
version = 3.12
print(f"Welcome to {name} {version}")
```

_exec_
```python
python -c "name = 'Python'; version = 3.12; print(f'Welcome to {name} {version}')"
```

_output_
```python
Welcome to Python 3.12
```

Demonstrates variable assignment and f-string printing.

- Python uses meaningful whitespace and indentation.
- Variables are dynamically typed.

#### Comments and multiline strings

```python
# This is a single-line comment

"""
This is a multiline comment
or docstring
"""

message = "Hello"  # inline comment
print(message)
```

_exec_
```python
python -c "message = 'Hello'; print(message)"
```

_output_
```python
Hello
```

Shows different comment styles and how to write docstrings.

- Use '#' for single-line comments.
- Use triple quotes for multiline comments or docstrings.

**Best practices:**

- Keep variable names descriptive and lowercase.
- Use comments to explain the "why", not the "what".
- Maintain consistent indentation (typically 4 spaces).

**Common errors:**

- **IndentationError when defining blocks**: Ensure consistent indentation, typically 4 spaces per level.
- **NameError for undefined variables**: Declare variables before using them.

### Data Types

Understanding Python's built-in data types and type conversion.

**Keywords:** data types, int, float, str, bool, type casting

#### Type casting and checking

```python
x = 42
y = float(x)
z = str(x)
print(type(x), type(y), type(z))
print(y, z)
```

_exec_
```python
python -c "x = 42; y = float(x); z = str(x); print(type(x), type(y), type(z)); print(y, z)"
```

_output_
```python
<class 'int'> <class 'float'> <class 'str'>
42.0 42
```

Shows type casting and how to check variable types.

- int(), float(), str(), bool() convert between types.
- type() returns the data type of a variable.

#### None and boolean types

```python
a = None
b = True
c = False
print(type(a), type(b), type(c))
print(bool(1), bool(0), bool(""))
```

_exec_
```python
python -c "a = None; b = True; c = False; print(bool(1), bool(0), bool(''))"
```

_output_
```python
True False False
```

Demonstrates None, boolean types, and truthiness.

- None represents a null value.
- Empty sequences and 0 evaluate to False.

**Best practices:**

- Use explicit type conversion to avoid unexpected behavior.
- Be aware of truthiness rules in conditionals.

**Common errors:**

- **TypeError when operating on incompatible types**: Use type casting to convert before operations.

### Numbers and Strings

Arithmetic operations, string slicing, and string methods.

**Keywords:** arithmetic, numbers, strings, slicing, string methods

#### Arithmetic operations and string slicing

```python
x = 10
y = 3
print(x + y, x - y, x * y, x / y, x // y, x % y, x ** y)

text = "Python"
print(text[0:3], text[-2:], text[::2])
```

_exec_
```python
python -c "x = 10; y = 3; print(x + y, x - y, x * y, x / y, x // y, x % y, x ** y); text = 'Python'; print(text[0:3], text[-2:], text[::2])"
```

_output_
```python
13 7 30 3.3333333333333335 3 1 1000
Pyt on Pto
```

Shows arithmetic operators and string slicing with start:end:step syntax.

- / performs float division, // performs integer division.
- Negative indices count from the end of the string.

#### String methods

```python
text = "HELLO WORLD"
print(text.lower())
print(text.replace("WORLD", "Python"))
print(text.split())
print("_".join(["a", "b", "c"]))
```

_exec_
```python
python -c "text = 'HELLO WORLD'; print(text.lower()); print(text.replace('WORLD', 'Python')); print(text.split()); print('_'.join(['a', 'b', 'c']))"
```

_output_
```python
hello world
HELLO Python
['HELLO', 'WORLD']
a_b_c
```

Demonstrates common string methods like lower(), replace(), split(), and join().

- String methods are case-sensitive for the method call.
- split() returns a list of strings.

**Best practices:**

- Use f-strings for modern string formatting.
- Remember that strings are immutable in Python.

**Common errors:**

- **IndexError when slicing beyond string length**: Python slicing is safe and won't raise errors for out-of-range indices.

## Data Structures

Working with lists, tuples, dictionaries, and sets.

### Lists

Creating and manipulating ordered, mutable sequences.

**Keywords:** lists, append, pop, insert, remove, indexing

#### List creation and indexing

```python
items = [1, 2, 3, 4, 5]
print(items[0], items[-1])
print(items[1:3], items[::2])
items.append(6)
print(items)
```

_exec_
```python
python -c "items = [1, 2, 3, 4, 5]; print(items[0], items[-1]); print(items[1:3], items[::2]); items.append(6); print(items)"
```

_output_
```python
1 5
[2, 3] [1, 3, 5]
[1, 2, 3, 4, 5, 6]
```

Demonstrates list creation, indexing, slicing, and the append() method.

- Lists are mutable and can be modified after creation.
- Use negative indices to access from the end.

#### List methods and operations

```python
items = [1, 2, 3, 2, 4]
items.remove(2)
print(items)
items.pop()
print(items)
items.extend([5, 6])
print(items)
```

_exec_
```python
python -c "items = [1, 2, 3, 2, 4]; items.remove(2); print(items); items.pop(); print(items); items.extend([5, 6]); print(items)"
```

_output_
```python
[1, 3, 2, 4]
[1, 3, 2]
[1, 3, 2, 5, 6]
```

Shows remove(), pop(), and extend() methods.

- remove() removes the first occurrence of a value.
- pop() removes and returns the last element.
- extend() adds multiple elements to the list.

**Best practices:**

- Use list comprehensions for creating filtered or transformed lists.
- Prefer extend() over append() when adding multiple items.

**Common errors:**

- **IndexError when accessing out-of-range indices**: Check list length or use list slicing which is safe.

### Tuples

Immutable sequences and tuple unpacking.

**Keywords:** tuples, immutable, unpacking, fixed size

#### Tuple creation and unpacking

```python
point = (10, 20)
x, y = point
print(f"x={x}, y={y}")

data = (1, 2, 3)
print(data[0], data[-1], len(data))
```

_exec_
```python
python -c "point = (10, 20); x, y = point; print(f'x={x}, y={y}'); data = (1, 2, 3); print(data[0], data[-1], len(data))"
```

_output_
```python
x=10, y=20
1 3 3
```

Creates tuples and demonstrates unpacking values into variables.

- Tuples are immutable; they cannot be modified after creation.
- Unpacking allows assigning tuple values to multiple variables.

#### Tuple operations

```python
t1 = (1, 2)
t2 = (3, 4)
t3 = t1 + t2
print(t3)
print(t3 * 2)
print((5,) == (5))
```

_exec_
```python
python -c "t1 = (1, 2); t2 = (3, 4); t3 = t1 + t2; print(t3); print(t3 * 2); print((5,) == (5))"
```

_output_
```python
(1, 2, 3, 4)
(1, 2, 3, 4, 1, 2, 3, 4)
False
```

Demonstrates tuple concatenation and repetition.

- Use a trailing comma (5,) to create a single-element tuple.

**Best practices:**

- Use tuples when you need immutable sequences.
- Use tuple unpacking to improve code readability.

**Common errors:**

- **TypeError when trying to modify a tuple**: Tuples are immutable; create a new tuple instead.

### Dictionaries

Key-value pairs and dictionary operations.

**Keywords:** dictionaries, dict, keys, values, items, get

#### Dictionary creation and access

```python
person = {"name": "Alice", "age": 30, "city": "NYC"}
print(person["name"])
print(person.get("age"))
print(person.get("email", "Not found"))
```

_exec_
```python
python -c "person = {'name': 'Alice', 'age': 30, 'city': 'NYC'}; print(person['name']); print(person.get('age')); print(person.get('email', 'Not found'))"
```

_output_
```python
Alice
30
Not found
```

Shows dictionary creation and access using [] and get().

- Use get() to safely access keys with a default value.
- get() returns None if the key is not found and no default is provided.

#### Dictionary modification and iteration

```python
d = {"a": 1, "b": 2}
d["c"] = 3
del d["a"]
print(d)
print(d.keys(), d.values())
for key, value in d.items():
  print(f"{key}: {value}")
```

_exec_
```python
python -c "d = {'a': 1, 'b': 2}; d['c'] = 3; del d['a']; print(d); print(list(d.keys()), list(d.values())); [(print(f'{k}: {v}')) for k, v in d.items()]"
```

_output_
```python
{'b': 2, 'c': 3}
dict_keys(['b', 'c']) dict_values([2, 3])
b: 2
c: 3
```

Demonstrates adding, deleting, and iterating over dictionary entries.

- Use del to remove key-value pairs.
- items() returns tuples of (key, value) pairs.

**Best practices:**

- Use get() to safely access dictionary values.
- Use dictionary comprehensions for creating dictionaries.

**Common errors:**

- **KeyError when accessing non-existent keys**: Use get() or check if key exists with 'in'.

### Sets

Unordered collections of unique elements.

**Keywords:** sets, unique, union, intersection, difference

#### Set creation and operations

```python
s = {1, 2, 3, 3, 4}
print(s)
s.add(5)
s.remove(2)
print(s)
```

_exec_
```python
python -c "s = {1, 2, 3, 3, 4}; print(s); s.add(5); s.remove(2); print(s)"
```

_output_
```python
{1, 2, 3, 4}
{1, 3, 4, 5}
```

Shows set creation (duplicates removed) and add/remove operations.

- Sets automatically remove duplicates.
- Sets are unordered, so iteration order is not guaranteed.

#### Set operations

```python
a = {1, 2, 3}
b = {3, 4, 5}
print(a | b)  # union
print(a & b)  # intersection
print(a - b)  # difference
```

_exec_
```python
python -c "a = {1, 2, 3}; b = {3, 4, 5}; print(a | b); print(a & b); print(a - b)"
```

_output_
```python
{1, 2, 3, 4, 5}
{3}
{1, 2}
```

Demonstrates union (|), intersection (&), and difference (-) operations.

- Use pipes |, ampersand &, and minus - for set operations.

**Best practices:**

- Use sets to remove duplicates from lists.
- Use sets for membership testing (faster than lists).

**Common errors:**

- **KeyError when removing non-existent elements**: Use discard() instead of remove() for safe removal.

## Control Flow

Conditionals, loops, and list comprehensions.

### Conditionals

if/elif/else statements and logical operators.

**Keywords:** conditionals, if, elif, else, comparison, logical operators

#### if/elif/else statements

```python
x = 15
if x < 10:
  print("Less than 10")
elif x < 20:
  print("Between 10 and 20")
else:
  print("20 or more")
```

_exec_
```python
python -c "x = 15; print('Between 10 and 20' if x >= 10 and x < 20 else ('Less than 10' if x < 10 else '20 or more'))"
```

_output_
```python
Between 10 and 20
```

Shows if/elif/else conditional logic.

- Each conditional block must be indented.
- Use elif for multiple conditions.

#### Comparison and logical operators

```python
x = 5
print(x > 3 and x < 10)
print(x == 5 or x == 10)
print(not (x == 0))
print(x in [1, 2, 5, 10])
```

_exec_
```python
python -c "x = 5; print(x > 3 and x < 10); print(x == 5 or x == 10); print(not (x == 0)); print(x in [1, 2, 5, 10])"
```

_output_
```python
True
True
True
True
```

Demonstrates logical operators (and, or, not) and membership testing (in).

- Use 'and', 'or', 'not' for boolean operations.
- Use 'in' to check membership in lists, strings, etc.

**Best practices:**

- Keep conditional logic simple and readable.
- Avoid deeply nested conditionals.

**Common errors:**

- **IndentationError in conditional blocks**: Maintain consistent indentation (4 spaces) for conditional blocks.

### Loops

for and while loops with break and continue.

**Keywords:** loops, for, while, break, continue, range

#### for loop with range and lists

```python
for i in range(3):
  print(f"Iteration {i}")

for item in ["a", "b", "c"]:
  print(item)
```

_exec_
```python
python -c "for i in range(3): print(f'Iteration {i}'); print('---'); [print(item) for item in ['a', 'b', 'c']]"
```

_output_
```python
Iteration 0
Iteration 1
Iteration 2
---
a
b
c
```

Shows for loops iterating over ranges and lists.

- range(n) generates numbers from 0 to n-1.
- for loops automatically iterate over sequences.

#### Loop control with break and continue

```python
for i in range(5):
  if i == 2:
    continue
  if i == 4:
    break
  print(i)
```

_exec_
```python
python -c "for i in range(5):\n  if i == 2:\n    continue\n  if i == 4:\n    break\n  print(i)"
```

_output_
```python
0
1
3
```

Demonstrates break (exit loop) and continue (skip iteration).

- continue skips the current iteration.
- break exits the loop entirely.

**Best practices:**

- Use for loops for iterating over sequences.
- Use while loops for condition-based repetition.

**Common errors:**

- **Infinite loop with while True**: Ensure a break condition or counter to exit the loop.

### List Comprehensions

Concise syntax for creating and filtering lists.

**Keywords:** list comprehension, list, filter, transform, nested

#### Basic list comprehension

```python
squares = [x**2 for x in range(5)]
print(squares)

evens = [x for x in range(10) if x % 2 == 0]
print(evens)
```

_exec_
```python
python -c "squares = [x**2 for x in range(5)]; print(squares); evens = [x for x in range(10) if x % 2 == 0]; print(evens)"
```

_output_
```python
[0, 1, 4, 9, 16]
[0, 2, 4, 6, 8]
```

Shows list comprehension with transformation and filtering.

- List comprehensions are more concise than for loops.
- Add if clause for filtering elements.

#### Nested list comprehension

```python
matrix = [[i*j for j in range(3)] for i in range(3)]
print(matrix)

flat = [x for row in matrix for x in row]
print(flat)
```

_exec_
```python
python -c "matrix = [[i*j for j in range(3)] for i in range(3)]; print(matrix); flat = [x for row in matrix for x in row]; print(flat)"
```

_output_
```python
[[0, 0, 0], [0, 1, 2], [0, 2, 4]]
[0, 0, 0, 0, 1, 2, 0, 2, 4]
```

Demonstrates nested list comprehensions and flattening matrices.

- Nested comprehensions can be complex; prioritize readability.

**Best practices:**

- Use list comprehensions for simple transformations.
- Break complex comprehensions into multiple lines for readability.

**Common errors:**

- **Complex comprehensions that are hard to read**: Convert back to for loops if comprehension becomes too complex.

## Functions and Scope

Defining functions, arguments, and variable scope.

### Function Definition

def keyword, parameters, return statements, and docstrings.

**Keywords:** function, def, return, docstring, parameter

#### Basic function definition

```python
def greet(name):
  """Greet a person by name."""
  return f"Hello, {name}!"

print(greet("Alice"))
```

_exec_
```python
python -c "def greet(name):\n  return f'Hello, {name}!'\nprint(greet('Alice'))"
```

_output_
```python
Hello, Alice!
```

Demonstrates basic function definition with parameters and return statement.

- Functions are defined with the def keyword.
- Docstrings document function purpose and usage.

#### Function with default arguments

```python
def add(a, b=0, c=0):
  return a + b + c

print(add(1))
print(add(1, 2))
print(add(1, 2, 3))
```

_exec_
```python
python -c "def add(a, b=0, c=0):\n  return a + b + c\nprint(add(1)); print(add(1, 2)); print(add(1, 2, 3))"
```

_output_
```python
1
3
6
```

Shows functions with default parameter values.

- Default parameters must come after required parameters.
- Default values are evaluated once at function definition.

**Best practices:**

- Write clear, concise functions with single responsibilities.
- Use docstrings to document function behavior.

**Common errors:**

- **UnboundLocalError when accessing outer variable**: Use nonlocal or global keywords as needed.

### Arguments

Positional arguments, keyword arguments, *args, and **kwargs.

**Keywords:** arguments, positional, keyword, *args, **kwargs, variadic

#### Positional and keyword arguments

```python
def describe(name, age, city):
  return f"{name} is {age} years old and lives in {city}"

print(describe("Alice", 30, "NYC"))
print(describe(name="Bob", city="LA", age=25))
```

_exec_
```python
python -c "def describe(name, age, city):\n  return f'{name} is {age} years old and lives in {city}'\nprint(describe('Alice', 30, 'NYC')); print(describe(name='Bob', city='LA', age=25))"
```

_output_
```python
Alice is 30 years old and lives in NYC
Bob is 25 years old and lives in LA
```

Shows positional and keyword argument passing.

- Keyword arguments can be passed in any order.
- Mix positional and keyword arguments by passing positional first.

#### Variable length arguments (*args and **kwargs)

```python
def sum_numbers(*args):
  return sum(args)

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

print(sum_numbers(1, 2, 3, 4))
print_info(name="Alice", age=30, city="NYC")
```

_exec_
```python
python -c "def sum_numbers(*args):\n  return sum(args)\ndef print_info(**kwargs):\n  for key, value in kwargs.items():\n    print(f'{key}: {value}')\nprint(sum_numbers(1, 2, 3, 4)); print_info(name='Alice', age=30, city='NYC')"
```

_output_
```python
10
name: Alice
age: 30
city: NYC
```

Demonstrates *args for variable positional arguments and **kwargs for variable keyword arguments.

- *args collects positional arguments as a tuple.
- **kwargs collects keyword arguments as a dictionary.

**Best practices:**

- Use *args and **kwargs for flexible function signatures.
- Document what arguments your function expects.

**Common errors:**

- **TypeError due to incorrect number of arguments**: Check function signature and ensure correct argument passing.

### Variable Scope

Local, global, and nonlocal variables.

**Keywords:** scope, local, global, nonlocal, namespace

#### Local and global scope

```python
x = "global"

def func():
  x = "local"
  print(x)

func()
print(x)
```

_exec_
```python
python -c "x = 'global'\ndef func():\n  x = 'local'\n  print(x)\nfunc(); print(x)"
```

_output_
```python
local
global
```

Shows local variables shadowing global variables.

- Local variables are created when assigned in a function.
- Accessing a global variable requires the global keyword to modify it.

#### Global and nonlocal keywords

```python
x = 0

def outer():
  y = 1
  def inner():
    nonlocal y
    global x
    x = 10
    y = 2
  inner()
  print(f"y={y}")

outer()
print(f"x={x}")
```

_exec_
```python
python -c "x = 0\ndef outer():\n  y = 1\n  def inner():\n    nonlocal y\n    global x\n    x = 10\n    y = 2\n  inner()\n  print(f'y={y}')\nouter(); print(f'x={x}')"
```

_output_
```python
y=2
x=10
```

Demonstrates global and nonlocal keywords to modify variables from enclosing scopes.

- global allows modifying module-level variables.
- nonlocal allows modifying variables in enclosing function scopes.

**Best practices:**

- Minimize use of global variables for code clarity.
- Prefer passing arguments and returning values.

**Common errors:**

- **UnboundLocalError accessing variable before assignment**: Use global or nonlocal keywords to access outer scope variables.

## Object-Oriented Programming

Classes, inheritance, and special methods.

### Classes

Class definition, __init__, self, and instance methods.

**Keywords:** class, object, __init__, self, method, instance

#### Basic class definition

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

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

dog = Dog("Buddy", 3)
print(dog.bark())
```

_exec_
```python
python -c "class Dog:\n  def __init__(self, name, age):\n    self.name = name\n    self.age = age\n  def bark(self):\n    return f'{self.name} says Woof!'\ndog = Dog('Buddy', 3); print(dog.bark())"
```

_output_
```python
Buddy says Woof!
```

Demonstrates class definition with __init__ constructor and instance methods.

- __init__ is the constructor called when creating an object.
- self refers to the instance and must be the first parameter.

#### Instance attributes and methods

```python
class Counter:
  def __init__(self):
    self.count = 0

  def increment(self):
    self.count += 1
    return self.count

c = Counter()
print(c.increment())
print(c.increment())
print(c.count)
```

_exec_
```python
python -c "class Counter:\n  def __init__(self):\n    self.count = 0\n  def increment(self):\n    self.count += 1\n    return self.count\nc = Counter(); print(c.increment()); print(c.increment()); print(c.count)"
```

_output_
```python
1
2
2
```

Shows instance attributes and methods that modify object state.

- Instance attributes are created in __init__ or assigned later.
- Methods can access and modify instance attributes.

**Best practices:**

- Use clear, descriptive class and method names.
- Initialize all instance attributes in __init__.

**Common errors:**

- **TypeError when forgetting self parameter**: Always include self as the first parameter in methods.

### Inheritance

Class inheritance, super(), and method overriding.

**Keywords:** inheritance, parent, child, super, override, extend

#### Class inheritance and super()

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

  def speak(self):
    return f"{self.name} makes a sound"

class Dog(Animal):
  def speak(self):
    return f"{self.name} barks"

dog = Dog("Rex")
print(dog.speak())
```

_exec_
```python
python -c "class Animal:\n  def __init__(self, name):\n    self.name = name\n  def speak(self):\n    return f'{self.name} makes a sound'\nclass Dog(Animal):\n  def speak(self):\n    return f'{self.name} barks'\ndog = Dog('Rex'); print(dog.speak())"
```

_output_
```python
Rex barks
```

Shows class inheritance and method overriding.

- Child classes inherit attributes and methods from parent classes.
- Override methods by redefining them in the child class.

#### Using super() to call parent methods

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

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

dog = Dog("Buddy", "Golden")
print(f"{dog.name} is a {dog.breed}")
```

_exec_
```python
python -c "class Animal:\n  def __init__(self, name):\n    self.name = name\nclass Dog(Animal):\n  def __init__(self, name, breed):\n    super().__init__(name)\n    self.breed = breed\ndog = Dog('Buddy', 'Golden'); print(f'{dog.name} is a {dog.breed}')"
```

_output_
```python
Buddy is a Golden
```

Demonstrates super() to call parent class methods.

- super() provides access to parent class methods.
- Always call super().__init__() to initialize parent attributes.

**Best practices:**

- Use inheritance for code reuse and logical hierarchies.
- Prefer composition over inheritance when appropriate.

**Common errors:**

- **TypeError in super().__init__() call**: Ensure parent class has __init__ and correct parameters.

### Special Methods

__str__, __repr__, __len__, __getitem__, and other dunder methods.

**Keywords:** dunder method, special method, __str__, __repr__, __len__, __getitem__

#### __str__ and __repr__ methods

```python
class Point:
  def __init__(self, x, y):
    self.x = x
    self.y = y

  def __str__(self):
    return f"Point({self.x}, {self.y})"

  def __repr__(self):
    return f"Point(x={self.x}, y={self.y})"

p = Point(3, 4)
print(str(p))
print(repr(p))
```

_exec_
```python
python -c "class Point:\n  def __init__(self, x, y):\n    self.x = x\n    self.y = y\n  def __str__(self):\n    return f'Point({self.x}, {self.y})'\np = Point(3, 4); print(str(p))"
```

_output_
```python
Point(3, 4)
```

Shows __str__ for user-friendly representation and __repr__ for development.

- __str__ returns a user-friendly string representation.
- __repr__ returns a developer-friendly representation (ideally recreatable).

#### __len__ and __getitem__ methods

```python
class SimpleList:
  def __init__(self, items):
    self.items = items

  def __len__(self):
    return len(self.items)

  def __getitem__(self, index):
    return self.items[index]

lst = SimpleList([1, 2, 3])
print(len(lst))
print(lst[0])
print(lst[-1])
```

_exec_
```python
python -c "class SimpleList:\n  def __init__(self, items):\n    self.items = items\n  def __len__(self):\n    return len(self.items)\n  def __getitem__(self, index):\n    return self.items[index]\nlst = SimpleList([1, 2, 3]); print(len(lst)); print(lst[0]); print(lst[-1])"
```

_output_
```python
3
1
3
```

Demonstrates __len__ for len() and __getitem__ for indexing.

- __len__ enables len() function on custom objects.
- __getitem__ enables indexing and slicing.

**Best practices:**

- Implement __str__ for readable output.
- Use special methods to make objects behave like built-in types.

**Common errors:**

- **Objects not indexable when __getitem__ is missing**: Implement __getitem__ to enable indexing.

## String Operations

Slicing, methods, formatting, and regular expressions.

### String Slicing

String slicing with start:end:step and negative indexing.

**Keywords:** slicing, string, indexing, substring, reverse

#### String slicing with indices

```python
s = "Python"
print(s[0:3])
print(s[3:])
print(s[-3:])
print(s[::2])
print(s[::-1])
```

_exec_
```python
python -c "s = 'Python'; print(s[0:3]); print(s[3:]); print(s[-3:]); print(s[::2]); print(s[::-1])"
```

_output_
```python
Pyt
hon
thon
Pto
nohtyP
```

Demonstrates string slicing with positive/negative indices and step values.

- s[start:end] slices from start to end-1.
- Negative indices count from the end.
- Step value (third parameter) allows skipping characters.

#### Advanced slicing techniques

```python
s = "Hello World"
print(s[6:11])
print(s[-5:])
print(s[::3])
print(s[1::2])
```

_exec_
```python
python -c "s = 'Hello World'; print(s[6:11]); print(s[-5:]); print(s[::3]); print(s[1::2])"
```

_output_
```python
World
World
HloWrd
elo ol
```

Shows practical slicing use cases.

- Slicing is safe even with out-of-range indices.

**Best practices:**

- Use negative indices for accessing from the end.
- Reverse strings with [::-1].

**Common errors:**

- **Off-by-one errors in slicing**: Remember that end index is exclusive.

### String Methods

Methods like upper(), lower(), split(), join(), replace(), find(), strip().

**Keywords:** string methods, upper, lower, split, join, replace, find, strip

#### Case conversion and whitespace handling

```python
s = "  Hello World  "
print(s.upper())
print(s.lower())
print(s.strip())
print(s.lstrip())
print(s.rstrip())
```

_exec_
```python
python -c "s = '  Hello World  '; print(s.upper()); print(s.lower()); print(s.strip()); print(repr(s.lstrip())); print(repr(s.rstrip()))"
```

_output_
```python
  HELLO WORLD  
  hello world  
Hello World
'Hello World  '
'  Hello World'
```

Shows case conversion and whitespace removal methods.

- strip() removes leading and trailing whitespace.
- lstrip() and rstrip() remove from left and right only.

#### String splitting, joining, and replacement

```python
text = "apple,banana,cherry"
items = text.split(",")
print(items)
print("-".join(items))
print(text.replace("apple", "orange"))
print(text.find("banana"))
```

_exec_
```python
python -c "text = 'apple,banana,cherry'; items = text.split(','); print(items); print('-'.join(items)); print(text.replace('apple', 'orange')); print(text.find('banana'))"
```

_output_
```python
['apple', 'banana', 'cherry']
apple-banana-cherry
orange,banana,cherry
6
```

Demonstrates split(), join(), replace(), and find() methods.

- split() returns a list of substrings.
- join() concatenates list items with a separator.
- find() returns the index of substring or -1 if not found.

**Best practices:**

- Use split() and join() for string parsing and formatting.
- Use replace() for simple text substitutions.

**Common errors:**

- **Expecting string when split() returns a list**: Remember that split() returns a list, not a string.

### String Formatting

f-strings, .format(), % formatting, and string interpolation.

**Keywords:** formatting, f-string, format, interpolation, %

#### f-strings and .format() method

```python
name = "Alice"
age = 30
city = "NYC"

print(f"Name: {name}, Age: {age}, City: {city}")
print("Name: {}, Age: {}, City: {}".format(name, age, city))
print("Name: {0}, Age: {1}, City: {2}".format(name, age, city))
```

_exec_
```python
python -c "name = 'Alice'; age = 30; city = 'NYC'; print(f'Name: {name}, Age: {age}, City: {city}')"
```

_output_
```python
Name: Alice, Age: 30, City: NYC
```

Shows f-strings (modern) and .format() (older style) formatting.

- f-strings are preferred for their readability and performance.
- .format() provides indexed and named placeholders.

#### Formatting numbers and expressions

```python
pi = 3.14159
price = 19.95
x = 10

print(f"Pi: {pi:.2f}")
print(f"Price: ${price:.2f}")
print(f"Expression: {x * 2}")
print(f"Hex: {x:x}")
```

_exec_
```python
python -c "pi = 3.14159; price = 19.95; x = 10; print(f'Pi: {pi:.2f}'); print(f'Price: ${price:.2f}'); print(f'Expression: {x * 2}'); print(f'Hex: {x:x}')"
```

_output_
```python
Pi: 3.14
Price: $19.95
Expression: 20
Hex: a
```

Demonstrates formatting with precision, currency, and different bases.

- Use :f for float formatting with .2f for 2 decimal places.
- f-strings support full Python expressions inside {}.

**Best practices:**

- Use f-strings for modern Python (3.6+).
- Format numbers appropriately for their domain (money, percentages, etc.).

**Common errors:**

- **KeyError in .format() with wrong placeholder names**: Ensure placeholder names match argument names or indices.

### Regular Expressions

Pattern matching with re.match(), re.search(), re.sub(), re.compile().

**Keywords:** regex, regular expression, pattern, match, search, substitute

#### Basic pattern matching

```python
import re

text = "Hello 123"
print(re.match(r'\w+', text))
print(re.search(r'\d+', text))
print(re.findall(r'\w+', text))
```

_exec_
```python
python -c "import re; text = 'Hello 123'; m = re.match(r'\\w+', text); print(m.group() if m else None); m = re.search(r'\\d+', text); print(m.group() if m else None); print(re.findall(r'\\w+', text))"
```

_output_
```python
Hello
123
['Hello', '123']
```

Shows match(), search(), and findall() for pattern matching.

- match() checks at the beginning of the string.
- search() finds the first match anywhere in the string.
- findall() returns all matches as a list.

#### Pattern substitution and compilation

```python
import re

text = "The date is 2025-02-27"
print(re.sub(r'\d+', '#', text))

pattern = re.compile(r'[a-z]+')
print(pattern.findall("abc123def456"))
```

_exec_
```python
python -c "import re; text = 'The date is 2025-02-27'; print(re.sub(r'\\d+', '#', text)); pattern = re.compile(r'[a-z]+'); print(pattern.findall('abc123def456'))"
```

_output_
```python
The date is #-#-#
['abc', 'def']
```

Demonstrates sub() for replacement and compile() for reusable patterns.

- sub() replaces matches with a replacement string.
- compile() creates a pattern object for reuse.

**Best practices:**

- Use raw strings (r'...') for regex patterns to avoid escaping issues.
- Compile patterns if using them multiple times.

**Common errors:**

- **AttributeError when match() returns None**: Check if match is None before calling .group().

## File I/O and Advanced

File operations, context managers, exception handling, and higher-order functions.

### File Operations

open(), read(), write(), readlines(), and seek().

**Keywords:** file, open, read, write, readlines, seek

#### Reading and writing files

```python
# Writing to a file
with open("example.txt", "w") as f:
  f.write("Hello, Python!")

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

_exec_
```python
python -c "with open('/tmp/example.txt', 'w') as f: f.write('Hello, Python!'); f = open('/tmp/example.txt', 'r'); content = f.read(); f.close(); print(content)"
```

_output_
```python
Hello, Python!
```

Demonstrates writing to and reading from files.

- Use "w" mode for writing (overwrites existing file).
- Use "r" mode for reading.
- Always close files or use with statement.

#### Reading lines and file positioning

```python
# Writing multiple lines
with open("lines.txt", "w") as f:
  f.write("Line 1\nLine 2\nLine 3")

# Reading lines
with open("lines.txt", "r") as f:
  lines = f.readlines()
  for line in lines:
    print(line.strip())
```

_exec_
```python
python -c "with open('/tmp/lines.txt', 'w') as f: f.write('Line 1\\nLine 2\\nLine 3'); f = open('/tmp/lines.txt', 'r'); lines = f.readlines(); f.close(); [print(line.strip()) for line in lines]"
```

_output_
```python
Line 1
Line 2
Line 3
```

Shows readlines() for reading multiple lines.

- readlines() returns a list of lines with newline characters.
- strip() removes leading/trailing whitespace including newlines.

**Best practices:**

- Always use with statement for automatic file closing.
- Choose appropriate file modes (r, w, a, r+, etc.).

**Common errors:**

- **FileNotFoundError when reading non-existent files**: Check file path and ensure file exists before reading.

### Context Managers

Using with statement for automatic resource management.

**Keywords:** context manager, with, contextlib, resource management

#### Context manager with open()

```python
with open("data.txt", "w") as f:
  f.write("Important data")
  print(f.closed)
print(f.closed)
```

_exec_
```python
python -c "f = None; exec('with open(\\\"/tmp/data.txt\\\", \\\"w\\\") as f: f.write(\\\"Important data\\\"); print(f.closed)'); print(f.closed)"
```

_output_
```python
False
True
```

Shows how with statement automatically closes files.

- with statement ensures cleanup even if exceptions occur.
- File is closed when exiting the with block.

#### Custom context manager

```python
from contextlib import contextmanager

@contextmanager
def timer():
  import time
  start = time.time()
  yield
  print(f"Elapsed: {time.time() - start:.2f}s")

with timer():
  sum(range(1000000))
```

_exec_
```python
python -c "from contextlib import contextmanager; import time\n@contextmanager\ndef timer():\n  start = time.time()\n  yield\n  print(f'Elapsed: {time.time() - start:.2f}s')\nwith timer(): sum(range(1000000))"
```

_output_
```python
Elapsed: 0.01s
```

Demonstrates creating custom context managers with @contextmanager decorator.

- @contextmanager simplifies context manager creation.
- Code before yield runs on entry, after yield on exit.

**Best practices:**

- Always use with statement for file operations.
- Create custom context managers for resource management.

**Common errors:**

- **Resource not released without with statement**: Always use with statement for file operations.

### Exception Handling

try/except/else/finally blocks and raising exceptions.

**Keywords:** exception, try, except, else, finally, raise

#### try/except blocks

```python
try:
  x = int("not a number")
except ValueError as e:
  print(f"Error: {e}")

try:
  result = 10 / 0
except ZeroDivisionError:
  print("Cannot divide by zero")
```

_exec_
```python
python -c "try:\n  x = int('not a number')\nexcept ValueError as e:\n  print(f'Error: {e}'); try:\n  result = 10 / 0\nexcept ZeroDivisionError:\n  print('Cannot divide by zero')"
```

_output_
```python
Error: invalid literal for int() with base 10: 'not a number'
Cannot divide by zero
```

Shows try/except blocks for handling specific exceptions.

- except block executes if the specific exception occurs.
- Use 'as' to capture exception details.

#### try/except/else/finally

```python
try:
  x = 10 / 2
except ZeroDivisionError:
  print("Error")
else:
  print(f"Result: {x}")
finally:
  print("Cleanup")
```

_exec_
```python
python -c "try:\n  x = 10 / 2\nexcept ZeroDivisionError:\n  print('Error')\nelse:\n  print(f'Result: {x}')\nfinally:\n  print('Cleanup')"
```

_output_
```python
Result: 5.0
Cleanup
```

Demonstrates else (no exception) and finally (always executes) blocks.

- else block executes if no exception occurs.
- finally block always executes for cleanup.

**Best practices:**

- Catch specific exceptions, not generic Exception.
- Use finally for cleanup code that must always run.

**Common errors:**

- **Catching too broad exception types**: Catch specific exception types to avoid masking bugs.

### Lambdas and Higher-Order Functions

Lambda functions, map(), filter(), sorted(), and functional programming.

**Keywords:** lambda, map, filter, sorted, higher-order function, functional programming

#### Lambda functions and map()

```python
squares = list(map(lambda x: x**2, [1, 2, 3, 4]))
print(squares)

add = lambda x, y: x + y
print(add(5, 3))
```

_exec_
```python
python -c "squares = list(map(lambda x: x**2, [1, 2, 3, 4])); print(squares); add = lambda x, y: x + y; print(add(5, 3))"
```

_output_
```python
[1, 4, 9, 16]
8
```

Demonstrates lambda functions with map() for transformations.

- lambda x: expression creates an anonymous function.
- map() applies function to each element in a sequence.

#### filter() and sorted()

```python
numbers = [1, 2, 3, 4, 5, 6]
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)

data = [(3, "c"), (1, "a"), (2, "b")]
sorted_data = sorted(data, key=lambda x: x[1])
print(sorted_data)
```

_exec_
```python
python -c "numbers = [1, 2, 3, 4, 5, 6]; evens = list(filter(lambda x: x % 2 == 0, numbers)); print(evens); data = [(3, 'c'), (1, 'a'), (2, 'b')]; sorted_data = sorted(data, key=lambda x: x[1]); print(sorted_data)"
```

_output_
```python
[2, 4, 6]
[(1, 'a'), (2, 'b'), (3, 'c')]
```

Shows filter() for selecting elements and sorted() with custom sort keys.

- filter() keeps elements where the function returns True.
- sorted(key=...) allows custom sorting with a function.

**Best practices:**

- Use list comprehensions instead of map()/filter() for readability.
- Keep lambdas simple; use def for complex functions.

**Common errors:**

- **Forgetting to convert map/filter results to list**: Use list() to convert iterables to lists.
