Source profileQuality 80/100

affaan-m/ECC/skills/python-patterns/SKILL.md

python-patterns

Pythonic idioms, PEP 8 standards, type hints, and best practices for building robust, efficient, and maintainable Python applications.

Source repository stars
234,327
Declared platforms
0
Static risk flags
2
Last source update
2026-07-27
Source checked
2026-07-28

Decision brief

What it does—and where it fits

Idiomatic Python patterns and best practices for building robust, efficient, and maintainable applications.

Best for

    Not for

    • Remember: Python code should be readable, explicit, and follow the principle of least surprise. When in doubt, prioritize clarity over cleverness.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/affaan-m/ECC --skill "skills/python-patterns"
    Safe inspection promptEditorial

    Inspect the Agent Skill "python-patterns" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/skills/python-patterns/SKILL.md at commit 4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      Usage

      def getuser(userid: str) - User: user = db.finduser(userid) if not user: raise NotFoundError(f"User not found: {userid}") return user python

      def getuser(userid: str) - User: user = db.finduser(userid) if not user: raise NotFoundError(f"User not found: {userid}") return user python
    2. 02

      Each call to process() prints the call count

      python import concurrent.futures import threading

      python import concurrent.futures import threadingdef fetchurl(url: str) - str: """Fetch a URL (I/O-bound operation).""" import urllib.request with urllib.request.urlopen(url) as response: return response.read().decode()def fetchallurls(urls: list[str]) - dict[str, str]: """Fetch multiple URLs concurrently using threads.""" with concurrent.futures.ThreadPoolExecutor(maxworkers=10) as executor: futuretourl = {executor.submit(fetchurl, u…
    3. 03

      Good: slots reduces memory usage

      class Point: slots = ['x', 'y']

      class Point: slots = ['x', 'y']def init(self, x: float, y: float): self.x = x self.y = y python
    4. 04

      When to Activate

      Writing new Python code

      Writing new Python codeReviewing Python codeRefactoring existing Python code
    5. 05

      Core Principles

      Python prioritizes readability. Code should be obvious and easy to understand.

      Python prioritizes readability. Code should be obvious and easy to understand.

    Permission review

    Static risk signals and limitations

    Reads files

    low · line 198

    The documentation asks the agent to read local files, directories, or repositories.

    with open(path, 'r') as f:

    Reads files

    low · line 203

    The documentation asks the agent to read local files, directories, or repositories.

    f = open(path, 'r')

    Network access

    medium · line 443

    The documentation includes network, browsing, or remote request actions.

    """Fetch a URL (I/O-bound operation)."""

    Network access

    medium · line 445

    The documentation includes network, browsing, or remote request actions.

    with urllib.request.urlopen(url) as response:

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score80/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars234,327SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    affaan-m/ECC
    Skill path
    skills/python-patterns/SKILL.md
    Commit
    4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38
    License
    MIT
    Collected
    2026-07-28
    Default branch
    main
    View the original SKILL.md

    Python Development Patterns

    Idiomatic Python patterns and best practices for building robust, efficient, and maintainable applications.

    When to Activate

    • Writing new Python code
    • Reviewing Python code
    • Refactoring existing Python code
    • Designing Python packages/modules

    Core Principles

    1. Readability Counts

    Python prioritizes readability. Code should be obvious and easy to understand.

    # Good: Clear and readable
    def get_active_users(users: list[User]) -> list[User]:
        """Return only active users from the provided list."""
        return [user for user in users if user.is_active]
    
    
    # Bad: Clever but confusing
    def get_active_users(u):
        return [x for x in u if x.a]
    

    2. Explicit is Better Than Implicit

    Avoid magic; be clear about what your code does.

    # Good: Explicit configuration
    import logging
    
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
    )
    
    # Bad: Hidden side effects
    import some_module
    some_module.setup()  # What does this do?
    

    3. EAFP - Easier to Ask Forgiveness Than Permission

    Python prefers exception handling over checking conditions.

    # Good: EAFP style
    def get_value(dictionary: dict, key: str, default_value: Any = None) -> Any:
        try:
            return dictionary[key]
        except KeyError:
            return default_value
    
    # Bad: LBYL (Look Before You Leap) style
    def get_value(dictionary: dict, key: str, default_value: Any = None) -> Any:
        if key in dictionary:
            return dictionary[key]
        else:
            return default_value
    

    Type Hints

    Basic Type Annotations

    from typing import Optional, List, Dict, Any
    
    def process_user(
        user_id: str,
        data: Dict[str, Any],
        active: bool = True
    ) -> Optional[User]:
        """Process a user and return the updated User or None."""
        if not active:
            return None
        return User(user_id, data)
    

    Modern Type Hints (Python 3.9+)

    # Python 3.9+ - Use built-in types
    def process_items(items: list[str]) -> dict[str, int]:
        return {item: len(item) for item in items}
    
    # Python 3.8 and earlier - Use typing module
    from typing import List, Dict
    
    def process_items(items: List[str]) -> Dict[str, int]:
        return {item: len(item) for item in items}
    

    Type Aliases and TypeVar

    from typing import TypeVar, Union
    
    # Type alias for complex types
    JSON = Union[dict[str, Any], list[Any], str, int, float, bool, None]
    
    def parse_json(data: str) -> JSON:
        return json.loads(data)
    
    # Generic types
    T = TypeVar('T')
    
    def first(items: list[T]) -> T | None:
        """Return the first item or None if list is empty."""
        return items[0] if items else None
    

    Protocol-Based Duck Typing

    from typing import Protocol
    
    class Renderable(Protocol):
        def render(self) -> str:
            """Render the object to a string."""
    
    def render_all(items: list[Renderable]) -> str:
        """Render all items that implement the Renderable protocol."""
        return "\n".join(item.render() for item in items)
    

    Error Handling Patterns

    Specific Exception Handling

    # Good: Catch specific exceptions
    def load_config(path: str) -> Config:
        try:
            with open(path) as f:
                return Config.from_json(f.read())
        except FileNotFoundError as e:
            raise ConfigError(f"Config file not found: {path}") from e
        except json.JSONDecodeError as e:
            raise ConfigError(f"Invalid JSON in config: {path}") from e
    
    # Bad: Bare except
    def load_config(path: str) -> Config:
        try:
            with open(path) as f:
                return Config.from_json(f.read())
        except:
            return None  # Silent failure!
    

    Exception Chaining

    def process_data(data: str) -> Result:
        try:
            parsed = json.loads(data)
        except json.JSONDecodeError as e:
            # Chain exceptions to preserve the traceback
            raise ValueError(f"Failed to parse data: {data}") from e
    

    Custom Exception Hierarchy

    class AppError(Exception):
        """Base exception for all application errors."""
        pass
    
    class ValidationError(AppError):
        """Raised when input validation fails."""
        pass
    
    class NotFoundError(AppError):
        """Raised when a requested resource is not found."""
        pass
    
    # Usage
    def get_user(user_id: str) -> User:
        user = db.find_user(user_id)
        if not user:
            raise NotFoundError(f"User not found: {user_id}")
        return user
    

    Context Managers

    Resource Management

    # Good: Using context managers
    def process_file(path: str) -> str:
        with open(path, 'r') as f:
            return f.read()
    
    # Bad: Manual resource management
    def process_file(path: str) -> str:
        f = open(path, 'r')
        try:
            return f.read()
        finally:
            f.close()
    

    Custom Context Managers

    from contextlib import contextmanager
    
    @contextmanager
    def timer(name: str):
        """Context manager to time a block of code."""
        start = time.perf_counter()
        yield
        elapsed = time.perf_counter() - start
        print(f"{name} took {elapsed:.4f} seconds")
    
    # Usage
    with timer("data processing"):
        process_large_dataset()
    

    Context Manager Classes

    class DatabaseTransaction:
        def __init__(self, connection):
            self.connection = connection
    
        def __enter__(self):
            self.connection.begin_transaction()
            return self
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            if exc_type is None:
                self.connection.commit()
            else:
                self.connection.rollback()
            return False  # Don't suppress exceptions
    
    # Usage
    with DatabaseTransaction(conn):
        user = conn.create_user(user_data)
        conn.create_profile(user.id, profile_data)
    

    Comprehensions and Generators

    List Comprehensions

    # Good: List comprehension for simple transformations
    names = [user.name for user in users if user.is_active]
    
    # Bad: Manual loop
    names = []
    for user in users:
        if user.is_active:
            names.append(user.name)
    
    # Complex comprehensions should be expanded
    # Bad: Too complex
    result = [x * 2 for x in items if x > 0 if x % 2 == 0]
    
    # Good: Use a generator function
    def filter_and_transform(items: Iterable[int]) -> list[int]:
        result = []
        for x in items:
            if x > 0 and x % 2 == 0:
                result.append(x * 2)
        return result
    

    Generator Expressions

    # Good: Generator for lazy evaluation
    total = sum(x * x for x in range(1_000_000))
    
    # Bad: Creates large intermediate list
    total = sum([x * x for x in range(1_000_000)])
    

    Generator Functions

    def read_large_file(path: str) -> Iterator[str]:
        """Read a large file line by line."""
        with open(path) as f:
            for line in f:
                yield line.strip()
    
    # Usage
    for line in read_large_file("huge.txt"):
        process(line)
    

    Data Classes and Named Tuples

    Data Classes

    from dataclasses import dataclass, field
    from datetime import datetime
    
    @dataclass
    class User:
        """User entity with automatic __init__, __repr__, and __eq__."""
        id: str
        name: str
        email: str
        created_at: datetime = field(default_factory=datetime.now)
        is_active: bool = True
    
    # Usage
    user = User(
        id="123",
        name="Alice",
        email="alice@example.com"
    )
    

    Data Classes with Validation

    @dataclass
    class User:
        email: str
        age: int
    
        def __post_init__(self):
            # Validate email format
            if "@" not in self.email:
                raise ValueError(f"Invalid email: {self.email}")
            # Validate age range
            if self.age < 0 or self.age > 150:
                raise ValueError(f"Invalid age: {self.age}")
    

    Named Tuples

    from typing import NamedTuple
    
    class Point(NamedTuple):
        """Immutable 2D point."""
        x: float
        y: float
    
        def distance(self, other: 'Point') -> float:
            return ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5
    
    # Usage
    p1 = Point(0, 0)
    p2 = Point(3, 4)
    print(p1.distance(p2))  # 5.0
    

    Decorators

    Function Decorators

    import functools
    import time
    
    def timer(func: Callable) -> Callable:
        """Decorator to time function execution."""
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            start = time.perf_counter()
            result = func(*args, **kwargs)
            elapsed = time.perf_counter() - start
            print(f"{func.__name__} took {elapsed:.4f}s")
            return result
        return wrapper
    
    @timer
    def slow_function():
        time.sleep(1)
    
    # slow_function() prints: slow_function took 1.0012s
    

    Parameterized Decorators

    def repeat(times: int):
        """Decorator to repeat a function multiple times."""
        def decorator(func: Callable) -> Callable:
            @functools.wraps(func)
            def wrapper(*args, **kwargs):
                results = []
                for _ in range(times):
                    results.append(func(*args, **kwargs))
                return results
            return wrapper
        return decorator
    
    @repeat(times=3)
    def greet(name: str) -> str:
        return f"Hello, {name}!"
    
    # greet("Alice") returns ["Hello, Alice!", "Hello, Alice!", "Hello, Alice!"]
    

    Class-Based Decorators

    class CountCalls:
        """Decorator that counts how many times a function is called."""
        def __init__(self, func: Callable):
            functools.update_wrapper(self, func)
            self.func = func
            self.count = 0
    
        def __call__(self, *args, **kwargs):
            self.count += 1
            print(f"{self.func.__name__} has been called {self.count} times")
            return self.func(*args, **kwargs)
    
    @CountCalls
    def process():
        pass
    
    # Each call to process() prints the call count
    

    Concurrency Patterns

    Threading for I/O-Bound Tasks

    import concurrent.futures
    import threading
    
    def fetch_url(url: str) -> str:
        """Fetch a URL (I/O-bound operation)."""
        import urllib.request
        with urllib.request.urlopen(url) as response:
            return response.read().decode()
    
    def fetch_all_urls(urls: list[str]) -> dict[str, str]:
        """Fetch multiple URLs concurrently using threads."""
        with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
            future_to_url = {executor.submit(fetch_url, url): url for url in urls}
            results = {}
            for future in concurrent.futures.as_completed(future_to_url):
                url = future_to_url[future]
                try:
                    results[url] = future.result()
                except Exception as e:
                    results[url] = f"Error: {e}"
        return results
    

    Multiprocessing for CPU-Bound Tasks

    def process_data(data: list[int]) -> int:
        """CPU-intensive computation."""
        return sum(x ** 2 for x in data)
    
    def process_all(datasets: list[list[int]]) -> list[int]:
        """Process multiple datasets using multiple processes."""
        with concurrent.futures.ProcessPoolExecutor() as executor:
            results = list(executor.map(process_data, datasets))
        return results
    

    Async/Await for Concurrent I/O

    import asyncio
    
    async def fetch_async(url: str) -> str:
        """Fetch a URL asynchronously."""
        import aiohttp
        async with aiohttp.ClientSession() as session:
            async with session.get(url) as response:
                return await response.text()
    
    async def fetch_all(urls: list[str]) -> dict[str, str]:
        """Fetch multiple URLs concurrently."""
        tasks = [fetch_async(url) for url in urls]
        results = await asyncio.gather(*tasks, return_exceptions=True)
        return dict(zip(urls, results))
    

    Package Organization

    Standard Project Layout

    myproject/
    ├── src/
    │   └── mypackage/
    │       ├── __init__.py
    │       ├── main.py
    │       ├── api/
    │       │   ├── __init__.py
    │       │   └── routes.py
    │       ├── models/
    │       │   ├── __init__.py
    │       │   └── user.py
    │       └── utils/
    │           ├── __init__.py
    │           └── helpers.py
    ├── tests/
    │   ├── __init__.py
    │   ├── conftest.py
    │   ├── test_api.py
    │   └── test_models.py
    ├── pyproject.toml
    ├── README.md
    └── .gitignore
    

    Import Conventions

    # Good: Import order - stdlib, third-party, local
    import os
    import sys
    from pathlib import Path
    
    import requests
    from fastapi import FastAPI
    
    from mypackage.models import User
    from mypackage.utils import format_name
    
    # Good: Use isort for automatic import sorting
    # pip install isort
    

    init.py for Package Exports

    # mypackage/__init__.py
    """mypackage - A sample Python package."""
    
    __version__ = "1.0.0"
    
    # Export main classes/functions at package level
    from mypackage.models import User, Post
    from mypackage.utils import format_name
    
    __all__ = ["User", "Post", "format_name"]
    

    Memory and Performance

    Using slots for Memory Efficiency

    # Bad: Regular class uses __dict__ (more memory)
    class Point:
        def __init__(self, x: float, y: float):
            self.x = x
            self.y = y
    
    # Good: __slots__ reduces memory usage
    class Point:
        __slots__ = ['x', 'y']
    
        def __init__(self, x: float, y: float):
            self.x = x
            self.y = y
    

    Generator for Large Data

    # Bad: Returns full list in memory
    def read_lines(path: str) -> list[str]:
        with open(path) as f:
            return [line.strip() for line in f]
    
    # Good: Yields lines one at a time
    def read_lines(path: str) -> Iterator[str]:
        with open(path) as f:
            for line in f:
                yield line.strip()
    

    Avoid String Concatenation in Loops

    # Bad: O(n²) due to string immutability
    result = ""
    for item in items:
        result += str(item)
    
    # Good: O(n) using join
    result = "".join(str(item) for item in items)
    
    # Good: Using StringIO for building
    from io import StringIO
    
    buffer = StringIO()
    for item in items:
        buffer.write(str(item))
    result = buffer.getvalue()
    

    Python Tooling Integration

    Essential Commands

    # Code formatting
    black .
    isort .
    
    # Linting
    ruff check .
    pylint mypackage/
    
    # Type checking
    mypy .
    
    # Testing
    pytest --cov=mypackage --cov-report=html
    
    # Security scanning
    bandit -r .
    
    # Dependency management
    pip-audit
    safety check
    

    pyproject.toml Configuration

    [project]
    name = "mypackage"
    version = "1.0.0"
    requires-python = ">=3.9"
    dependencies = [
        "requests>=2.31.0",
        "pydantic>=2.0.0",
    ]
    
    [project.optional-dependencies]
    dev = [
        "pytest>=7.4.0",
        "pytest-cov>=4.1.0",
        "black>=23.0.0",
        "ruff>=0.1.0",
        "mypy>=1.5.0",
    ]
    
    [tool.black]
    line-length = 88
    target-version = ['py39']
    
    [tool.ruff]
    line-length = 88
    select = ["E", "F", "I", "N", "W"]
    
    [tool.mypy]
    python_version = "3.9"
    warn_return_any = true
    warn_unused_configs = true
    disallow_untyped_defs = true
    
    [tool.pytest.ini_options]
    testpaths = ["tests"]
    addopts = "--cov=mypackage --cov-report=term-missing"
    

    Quick Reference: Python Idioms

    IdiomDescription
    EAFPEasier to Ask Forgiveness than Permission
    Context managersUse with for resource management
    List comprehensionsFor simple transformations
    GeneratorsFor lazy evaluation and large datasets
    Type hintsAnnotate function signatures
    DataclassesFor data containers with auto-generated methods
    __slots__For memory optimization
    f-stringsFor string formatting (Python 3.6+)
    pathlib.PathFor path operations (Python 3.4+)
    enumerateFor index-element pairs in loops

    Anti-Patterns to Avoid

    # Bad: Mutable default arguments
    def append_to(item, items=[]):
        items.append(item)
        return items
    
    # Good: Use None and create new list
    def append_to(item, items=None):
        if items is None:
            items = []
        items.append(item)
        return items
    
    # Bad: Checking type with type()
    if type(obj) == list:
        process(obj)
    
    # Good: Use isinstance
    if isinstance(obj, list):
        process(obj)
    
    # Bad: Comparing to None with ==
    if value == None:
        process()
    
    # Good: Use is
    if value is None:
        process()
    
    # Bad: from module import *
    from os.path import *
    
    # Good: Explicit imports
    from os.path import join, exists
    
    # Bad: Bare except
    try:
        risky_operation()
    except:
        pass
    
    # Good: Specific exception
    try:
        risky_operation()
    except SpecificError as e:
        logger.error(f"Operation failed: {e}")
    

    Remember: Python code should be readable, explicit, and follow the principle of least surprise. When in doubt, prioritize clarity over cleverness.

    Alternatives

    Compare before choosing