$npx -y skills add sordi-ai/skill-everything --skill pythonApply when writing Python code. Type hints, error handling, mutable defaults, async patterns, and packaging conventions.
| 1 | # Sub-Skill: Python Best Practices |
| 2 | <!-- target: ~2200 tokens (real tiktoken count) | 20 rules with severity classification --> |
| 3 | |
| 4 | **Purpose:** Prevents the Python-specific mistakes LLMs make on autopilot — mutable defaults, bare excepts, missing guards, and subtle performance traps that pass review but break in production. |
| 5 | |
| 6 | ## Rule classification |
| 7 | |
| 8 | - **MUST** — load-bearing. Violating causes bugs, security issues, or invisible failures. Never break. |
| 9 | - **SHOULD** — default behavior. Deviation needs a documented reason in the code or PR. |
| 10 | - **AVOID** — usually wrong; documented exception inline where needed. |
| 11 | |
| 12 | **Where these rules don't strictly apply:** test fixtures, generated code, throwaway scripts, REPL exploration, and tutorial snippets may legitimately differ. The rules below apply to **production code paths and reusable libraries**. |
| 13 | |
| 14 | --- |
| 15 | |
| 16 | ## Rules |
| 17 | |
| 18 | ### Type Hints |
| 19 | |
| 20 | 1. **SHOULD: Annotate function signatures with types.** `def process(data)` tells callers nothing. Use `def process(data: list[str]) -> dict[str, int]:`. Return type `None` should be explicit too. *Exception: lambdas and short generator helpers in private scope.* |
| 21 | |
| 22 | 2. **SHOULD: Use built-in lowercase generics for Python 3.9+.** `list[str]`, `dict[str, int]`, `tuple[int, ...]` rather than `List`, `Dict`, `Tuple` from `typing`. |
| 23 | |
| 24 | 3. **MUST: Use `Optional[X]` or `X | None` for nullable parameters, never a bare default of `None` without a type annotation.** `def find(id: int) -> User | None:` not `def find(id):`. |
| 25 | |
| 26 | 4. **AVOID: `Any` as a shortcut.** If the type is genuinely unknown, document why with a comment. `Any` silences the type checker and hides bugs. *Exception: third-party libraries without stubs and explicit dynamic-data boundaries (e.g. JSON decode at the API edge).* |
| 27 | |
| 28 | --- |
| 29 | |
| 30 | ### Error Handling |
| 31 | |
| 32 | 5. **MUST: Never use bare `except:`.** It catches `SystemExit`, `KeyboardInterrupt`, and `GeneratorExit`. Always catch `except Exception:` at minimum, or a specific exception class. |
| 33 | |
| 34 | ```python |
| 35 | # Wrong |
| 36 | try: |
| 37 | risky() |
| 38 | except: |
| 39 | pass |
| 40 | |
| 41 | # Correct |
| 42 | try: |
| 43 | risky() |
| 44 | except ValueError as e: |
| 45 | logger.warning("Invalid value: %s", e) |
| 46 | ``` |
| 47 | |
| 48 | 6. **MUST: Never silently swallow exceptions with `pass`.** At minimum log the error. Silent failures produce ghost bugs that are impossible to trace. |
| 49 | |
| 50 | 7. **SHOULD: Raise with context when re-raising.** Use `raise NewError("msg") from original_error` to preserve the traceback chain, not `raise NewError("msg")` alone. |
| 51 | |
| 52 | --- |
| 53 | |
| 54 | ### Common Pitfalls |
| 55 | |
| 56 | 8. **MUST: Never use mutable default arguments.** Python evaluates defaults once at function definition, not per call. The list or dict is shared across all calls. |
| 57 | |
| 58 | ```python |
| 59 | # Wrong — items accumulates across calls |
| 60 | def append_item(val, items=[]): |
| 61 | items.append(val) |
| 62 | return items |
| 63 | |
| 64 | # Correct |
| 65 | def append_item(val, items=None): |
| 66 | if items is None: |
| 67 | items = [] |
| 68 | items.append(val) |
| 69 | return items |
| 70 | ``` |
| 71 | |
| 72 | 9. **MUST: Guard script entry points with `if __name__ == "__main__":`.** Without it, importing the module executes top-level code, breaking tests and imports. |
| 73 | |
| 74 | 10. **MUST: Use `with` for file handles, sockets, and locks.** Never open a file without a context manager. `f = open(...)` without `with` leaks handles on exceptions. |
| 75 | |
| 76 | ```python |
| 77 | # Wrong |
| 78 | f = open("data.txt") |
| 79 | data = f.read() |
| 80 | f.close() |
| 81 | |
| 82 | # Correct |
| 83 | with open("data.txt") as f: |
| 84 | data = f.read() |
| 85 | ``` |
| 86 | |
| 87 | 11. **AVOID: String concatenation in loops.** Each `+=` on a string creates a new object. Collect into a list and call `"".join(parts)` at the end. |
| 88 | |
| 89 | ```python |
| 90 | # Wrong — O(n^2) memory |
| 91 | result = "" |
| 92 | for word in words: |
| 93 | result += word + " " |
| 94 | |
| 95 | # Correct |
| 96 | result = " ".join(words) |
| 97 | ``` |
| 98 | |
| 99 | 12. **SHOULD: Use f-strings for string interpolation in Python 3.6+.** Avoid `%` formatting or `"Hello " + name`. F-strings are faster, safer, and readable. |
| 100 | |
| 101 | ```python |
| 102 | # Avoid |
| 103 | msg = "User %s has %d items" % (name, count) |
| 104 | |
| 105 | # Prefer |
| 106 | msg = f"User {name} has {count} items" |
| 107 | ``` |
| 108 | |
| 109 | 13. **AVOID: `dict()` constructor when a literal suffices.** `{}` is faster and more idiomatic. `dict(key=value)` is only justified when keys are dynamic or come from variables. |
| 110 | |
| 111 | --- |
| 112 | |
| 113 | ### Performance |
| 114 | |
| 115 | 14. **SHOULD: Use list comprehensions or generator expressions instead of `map`/`filter` with `lambda`.** Comprehensions are more readable and equally fast. Use generators when the full list is not needed at once. |
| 116 | |
| 117 | ```python |
| 118 | # Avoid |
| 119 | result = list(map(lambda x: x * 2, items)) |
| 120 | |
| 121 | # Prefer |
| 122 | result = [x * 2 for x in items] |
| 123 | |
| 124 | # For large data, use a generator |
| 125 | total = sum(x * 2 for x in items) |
| 126 | ``` |
| 127 | |
| 128 | 1 |