I. Functions - Definition, Parameters & Scope
Function Definition & Call
-
Defined using
def function_name(parameters): -
Called by
function_name(arguments)
Positional vs. Keyword Arguments
| Type | Description | Example |
|---|---|---|
| Positional | Arguments matched by order | func(1, 2) |
| Keyword | Arguments matched by parameter name | func(a=1, b=2) |
Default Arguments & Mutable Default Pitfalls
-
Default values assigned in definition:
def func(a=10): -
Critical Pitfall: Mutable defaults (e.g.,
[],{}) are created once at function definition, leading to shared state across calls.def bad_func(x=[]): # ❌ Dangerous x.append(1) return xFix: Use
Noneand initialize inside function.def good_func(x=None): # ✅ Safe if x is None: x = [] x.append(1) return x
Variable-Length Arguments
-
*args: Collects extra positional arguments into a tuple. -
**kwargs: Collects extra keyword arguments into a dict.def example(a, *args, **kwargs): print(a, args, kwargs) example(1, 2, 3, x=4, y=5) # Output: 1 (2, 3) {'x':4, 'y':5}
Return Statements
-
returnexits function, optionally returning value(s). -
Multiple values returned as tuple:
return a, b, c→(a, b, c).
Function Scope: LEGB Rule
\boxed{\text{LEGB Rule: Local → Enclosing → Global → Built-in}}
-
Local: Inside current function.
-
Enclosing: In enclosing (outer) functions.
-
Global: At module level.
-
Built-in: Python’s pre-defined names (e.g.,
len).
[!TIP]
Use
globalto modify global variables inside function;nonlocalfor enclosing scope variables.
Lambda Functions
-
Anonymous, single-expression functions:
lambda args: expression -
Typical use:
keyfunctions (e.g.,sorted(lst, key=lambda x: x[1])),map(),filter().
Recursion
-
Base case: Condition to stop recursion.
-
Recursive case: Function calls itself with modified arguments.
-
Stack limit: Python has recursion limit (default ~1000). Exceeding raises
RecursionError.
[!TIP]
Ensure base case is reachable; otherwise infinite recursion → stack overflow.
II. Modular Programming & Standard Library
Modules & Imports
-
import module: Access viamodule.func(). -
from module import func: Direct use offunc(). -
Aliasing:
import module as aliasorfrom module import func as alias.
Creating Custom Modules
-
Save code in
.pyfile (e.g.,mymodule.py). -
Import in another script:
import mymodule.
Packages
-
Directory containing
__init__.py(can be empty) + module files/sub-packages. -
Structure:
package/ ├── __init__.py ├── module1.py └── subpackage/ ├── __init__.py └── module2.py -
Import:
from package import module1orfrom package.subpackage import module2.
Essential Standard Library Modules
| Module | Primary Use Cases |
|---|---|
sys |
sys.argv (CLI args), sys.path (module search path), sys.exit() |
os |
os.getcwd(), os.listdir(), os.mkdir(), os.remove() |
os.path |
os.path.join(), os.path.exists(), os.path.split() (path manipulation) |
datetime |
datetime.now(), date, time, timedelta, timezone handling |
math |
math.pi, math.sqrt(), math.floor(), math.sin() |
random |
random.random(), randint(a,b), choice(seq), shuffle(seq) |
json |
json.loads() (string → object), json.dumps() (object → string) |
csv |
csv.reader() (read CSV), csv.writer() (write CSV) |
[!TIP]
Use
os.path.join()for cross-platform path construction instead of string concatenation.
III. File Input/Output (I/O) Operations
open() Function & File Modes
\boxed{\text{Common Modes: 'r', 'w', 'a', 'b', '+'}}
| Mode | Description | File Pointer | Creates File? |
|---|---|---|---|
r |
Read (default) | Start | No |
w |
Write (overwrite) | Start | Yes |
a |
Append | End | Yes |
b |
Binary mode (with other modes) | - | - |
+ |
Update (read/write) | - | - |
r+ |
Read & write from start | Start | No |
w+ |
Read & write (overwrite) | Start | Yes |
a+ |
Read & append | End | Yes |
Reading Files
-
f.read(size=-1): Read entire file orsizebytes. -
f.readline(): Read one line (including newline). -
f.readlines(): Read all lines into list. -
Iteration over file object: Memory-efficient line-by-line:
with open('file.txt') as f: for line in f: # ✅ Best for large files process(line)
Writing Files
-
f.write(string): Write string to file. -
f.writelines(list_of_strings): Write each string (no newline added automatically).
Context Managers (with statement)
\boxed{\text{Automatically closes file after block, even on exceptions}}
with open('file.txt', 'r') as f:
data = f.read()
# File automatically closed here
[!TIP]
Always use
withfor file operations to avoid resource leaks and ensure proper closure.
Text vs. Binary Files
-
Text mode (
'r','w', etc.): Returns/acceptsstr; handles encoding/decoding (default UTF-8). -
Binary mode (
'rb','wb'): Returns/acceptsbytes; no encoding; used for images, executables.
File Object Methods
-
f.seek(offset, whence=0): Move pointer (whence: 0=start, 1=current, 2=end). -
f.tell(): Return current pointer position. -
f.close(): Explicit close (not needed withwith).
IV. Exception Handling & Debugging
Exceptions vs. Errors
-
Exceptions: Raised during execution; can be caught/handled.
-
Errors: Often syntax or system-level; may not be catchable (e.g.,
SyntaxError).
Common Built-in Exceptions
| Exception | Typical Cause |
|---|---|
ValueError |
Right type, inappropriate value (e.g., int('abc')) |
TypeError |
Operation on wrong type (e.g., 'a' + 1) |
IOError/OSError |
File operation failure (e.g., file not found) |
KeyError |
Key not in dictionary |
IndexError |
Index out of range in sequence |
AttributeError |
Attribute/method not found on object |
ZeroDivisionError |
Division/modulo by zero |
try-except-else-finally Structure
\boxed{
\begin{aligned}
&\textbf{try:} &&\text{Code that may raise exception} \
&\textbf{except ExceptionType as e:} &&\text{Handle exception} \
&\textbf{else:} &&\text{Run if no exception in try} \
&\textbf{finally:} &&\text{Always run (cleanup)}
\end{aligned}
}
try:
result = 10 / 0
except ZeroDivisionError as e:
print("Cannot divide by zero:", e)
else:
print("Result:", result) # Skipped if exception
finally:
print("Cleanup complete") # Always executes
Catching Multiple Exceptions
-
Separate
exceptblocks:try: ... except ValueError: ... except TypeError: ... -
Single block for multiple:
except (ValueError, TypeError) as e:
Raising Exceptions
-
raise Exception("message") -
Custom exceptions: Inherit from
Exception(or subclass).class MyCustomError(Exception): pass raise MyCustomError("Something went wrong")
Assertions
-
assert condition, "message"→ raisesAssertionErrorifconditionisFalse. -
Used for debugging; removed with
python -O(optimized mode).
Basic Debugging
-
print()debugging: Insert prints to trace values. -
pdb: Python debugger. Insertimport pdb; pdb.set_trace()to start interactive debugging.Common commands:
n(next),s(step into),c(continue),p var(print variable).
[!TIP]
finallyruns even iftryhasreturnor exception caught. Use for cleanup (e.g., closing files, releasing locks).
V. Object-Oriented Programming (OOP) in Python
Classes & Objects
-
Class: Blueprint via
class ClassName: -
Object: Instance via
obj = ClassName() -
__init__(self, ...): Constructor; initializes instance attributes.class Person: def __init__(self, name, age): self.name = name # Instance attribute self.age = age
Instance vs. Class Attributes
-
Instance attributes:
self.attr; unique per object. -
Class attributes: Defined in class body; shared across all instances.
class Dog: species = "Canis lupus" # Class attribute (shared) def __init__(self, name): self.name = name # Instance attribute
Instance Methods & self
-
Instance methods: First parameter is
self(reference to instance). -
Called on object:
obj.method(args)→ Python passesobjasself.
Special (Dunder/Magic) Methods
| Method | Purpose | Called By |
|---|---|---|
__str__ |
Informal string (print) | str(obj), print(obj) |
__repr__ |
Official string (debugging) | repr(obj) |
__len__ |
Length of object | len(obj) |
__add__ |
Addition (+) |
obj1 + obj2 |
__getitem__ |
Indexing (obj[key]) |
obj[key] |
[!TIP]
Implement
__repr__to return unambiguous representation (ideallyClassName(attrs));__str__for readable output.
Inheritance
-
Base/Parent → Derived/Child:
class Child(Parent): -
super(): Access parent methods. In__init__:super().__init__(args). -
Method Resolution Order (MRO): Order Python searches for methods in inheritance hierarchy.
\boxed{\text{MRO follows C3 linearization; view via
ClassName.__mro__orhelp(ClassName)}}Example:
class A: pass class B(A): pass class C(A): pass class D(B, C): pass print(D.__mro__) # (D, B, C, A, object)
Polymorphism (Duck Typing)
-
"If it walks like a duck and quacks like a duck, it’s a duck."
-
No explicit interface; objects used based on available methods/attributes.
def make_sound(obj): obj.sound() # Works if obj has sound() method
Properties: @property Decorator
-
Getter/setter pattern without explicit method calls.
class Circle: def __init__(self, radius): self._radius = radius @property def radius(self): return self._radius @radius.setter def radius(self, value): if value <= 0: raise ValueError("Radius must be positive") self._radius = valueUsage:
c.radius(get),c.radius = 5(set).
Class Methods & Static Methods
-
@classmethod: First paramcls(class); used for factory methods, alternative constructors.class MyClass: @classmethod def from_string(cls, s): return cls(*parse(s)) -
@staticmethod: Noself/cls; like plain function but namespaced in class.class Math: @staticmethod def add(a, b): return a + b
Encapsulation & Naming Conventions
-
Public:
attr(no underscore). -
Protected (convention):
_attr→ internal use, but accessible. -
Private (name mangling):
__attr→ Python renames to_ClassName__attr; harder to access accidentally.
[!TIP]
Prefer
@propertyover getter/setter methods for attribute access control. Usesuper()in inherited__init__to ensure parent initialization.
VI. Pythonic Idioms & Advanced Features
Comprehensions
-
List:
[expr for item in iterable if condition]squares = [x**2 for x in range(10) if x % 2 == 0] -
Dict:
{key: value for key, value in iterable}sq_dict = {x: x**2 for x in range(5)} -
Set:
{expr for item in iterable}unique_squares = {x**2 for x in [1, 2, 2, 3]}
Generator Expressions vs. List Comprehensions
-
Generator:
(expr for item in iterable)→ lazy evaluation, yields items one-by-one. -
Memory: Generator uses less memory (no list stored); list comprehension creates full list.
sum(x**2 for x in range(1000000)) # ✅ Memory efficient sum([x**2 for x in range(1000000)]) # ❌ Creates large list
Iterators & Iterables
-
Iterable: Object with
__iter__()returning iterator (e.g., list, tuple, dict). -
Iterator: Object with
__next__()returning next item; raisesStopIterationat end.it = iter([1, 2, 3]) print(next(it)) # 1 print(next(it)) # 2 -
Iterator protocol:
forloop usesiter()andnext()implicitly.
Generator Functions
-
Defined with
yieldinstead ofreturn. -
State retention: Pauses at
yield, resumes from same point on next call.def countdown(n): while n > 0: yield n n -= 1 for i in countdown(3): print(i) # 3, 2, 1
enumerate() & zip()
-
enumerate(iterable, start=0): Yields(index, value)tuples.for i, val in enumerate(['a', 'b', 'c']): print(i, val) # 0 a, 1 b, 2 c -
zip(*iterables): Yields tuples pairing elements from each iterable.names = ['Alice', 'Bob'] scores = [85, 92] for name, score in zip(names, scores): print(name, score)
Unpacking
-
Tuple/List unpacking:
a, b = (1, 2) -
Extended unpacking:
first, *middle, last = [1,2,3,4]→first=1, middle=[2,3], last=4 -
Function args:
*args(tuple),**kwargs(dict) in calls.def func(a, b, c): print(a, b, c) args = (1, 2, 3) func(*args) # Unpacks to func(1,2,3)
Decorators (Brief Introduction)
-
Syntax:
@decoratorabove function/class definition. -
Simple function decorator: Wraps function to modify behavior.
def my_decorator(func): def wrapper(*args, **kwargs): print("Before call") result = func(*args, **kwargs) print("After call") return result return wrapper @my_decorator def say_hello(): print("Hello!") say_hello() # Output: # Before call # Hello! # After call
[!TIP]
Use generator expressions for large datasets to save memory. Master extended unpacking (
*,**) for clean data handling in functions.