Skip to content
CS-506 · Python/Quick Revision Short Notes

Python (CS-506) - Unit 3 Short Notes

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 x
    
    

    Fix: Use None and 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

  • return exits 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 global to modify global variables inside function; nonlocal for enclosing scope variables.

Lambda Functions

  • Anonymous, single-expression functions: lambda args: expression

  • Typical use: key functions (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 via module.func().

  • from module import func: Direct use of func().

  • Aliasing: import module as alias or from module import func as alias.

Creating Custom Modules

  • Save code in .py file (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 module1 or from 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 or size bytes.

  • 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 with for file operations to avoid resource leaks and ensure proper closure.

Text vs. Binary Files

  • Text mode ('r', 'w', etc.): Returns/accepts str; handles encoding/decoding (default UTF-8).

  • Binary mode ('rb', 'wb'): Returns/accepts bytes; 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 with with).


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 except blocks:

    
    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" → raises AssertionError if condition is False.

  • Used for debugging; removed with python -O (optimized mode).

Basic Debugging

  • print() debugging: Insert prints to trace values.

  • pdb: Python debugger. Insert import pdb; pdb.set_trace() to start interactive debugging.

    Common commands: n (next), s (step into), c (continue), p var (print variable).

[!TIP]

finally runs even if try has return or 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 passes obj as self.

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 (ideally ClassName(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__ or help(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 = value
    
    

    Usage: c.radius (get), c.radius = 5 (set).

Class Methods & Static Methods

  • @classmethod: First param cls (class); used for factory methods, alternative constructors.

    
    class MyClass:
    
        @classmethod
    
        def from_string(cls, s):
    
            return cls(*parse(s))
    
    
  • @staticmethod: No self/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 @property over getter/setter methods for attribute access control. Use super() 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; raises StopIteration at end.

    
    it = iter([1, 2, 3])
    
    print(next(it))  # 1
    
    print(next(it))  # 2
    
    
  • Iterator protocol: for loop uses iter() and next() implicitly.

Generator Functions

  • Defined with yield instead of return.

  • 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: @decorator above 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.

Go to where you left off?

Quick Add to Notes

Save questions, your own notes and screenshots into notes filed by unit. It takes a free account.

Create free account

Have an account? Log in