AI Skill Report Card
Computing Sequence Diffs
Computes and presents differences between sequences (text lines, strings, lists) using Python's difflib module. Use when comparing files, generating diffs, finding close string matches, computing similarity ratios, or building patch/merge-like output.
Quick Start15 / 15
Pythonimport difflib a = ["line1\n", "line2\n", "line3\n"] b = ["line1\n", "line2 modified\n", "line3\n"] diff = difflib.unified_diff(a, b, fromfile="a.txt", tofile="b.txt", lineterm="") print("\n".join(diff))
Output:
--- a.txt
+++ b.txt
@@ -1,3 +1,3 @@
line1
-line2
+line2 modified
line3
Recommendation▾
Add an example showing HtmlDiff or ndiff output to cover more of the decision table entries
Workflow14 / 15
Progress:
- Identify the comparison need: line-by-line diff, similarity score, or closest-match lookup
- Pick the right tool (see decision table below)
- Prepare inputs as sequences (lists of lines/strings), not raw multi-line strings
- Run the comparison
- Format/present output (unified, context, or HTML)
Decision table:
| Need | Tool |
|---|---|
| Git-style unified diff | difflib.unified_diff(a, b) |
| Context diff (before/after blocks) | difflib.context_diff(a, b) |
Human-readable inline diff (ndiff style, +/-/?) | difflib.ndiff(a, b) |
| Side-by-side HTML diff | difflib.HtmlDiff().make_file(a, b) |
| Similarity ratio between two strings (0.0–1.0) | difflib.SequenceMatcher(None, a, b).ratio() |
| Fuzzy match closest strings from a list | difflib.get_close_matches(word, possibilities) |
| Detailed opcodes (replace/insert/delete/equal ranges) | SequenceMatcher.get_opcodes() |
Steps:
- Read files with
.readlines()(keepends=True) so diffs preserve line structure — don't use.split("\n")unless you strip newlines consistently. - Choose the tool from the table.
- For
SequenceMatcher, passautojunk=Falseif comparing short/technical strings (default autojunk can skew results on repetitive text >200 chars). - Join generator output with
"\n".join(...)or iterate directly — these functions return generators/iterators, not lists.
Recommendation▾
Include a brief note on performance/alternatives (e.g., difflib vs. external libs like Levenshtein) for large-scale diffing
Examples18 / 20
Example 1: Similarity ratio Input:
Pythondifflib.SequenceMatcher(None, "python", "pytohn").ratio()
Output: 0.8333333333333334
Example 2: Closest match lookup Input:
Pythondifflib.get_close_matches("appel", ["ape", "apple", "peach", "puppy"])
Output: ['apple', 'ape']
Example 3: Opcodes for custom diff rendering Input:
Pythons = difflib.SequenceMatcher(None, "qabxcd", "abycdf") s.get_opcodes()
Output:
Python[('delete', 0, 1, 0, 0), ('equal', 1, 3, 0, 2), ('replace', 3, 4, 2, 3), ('equal', 4, 6, 3, 5), ('insert', 6, 6, 5, 6)]
Example 4: File diff (CLI-style)
Input: two files old.py, new.py
Pythonwith open("old.py") as f1, open("new.py") as f2: diff = difflib.unified_diff(f1.readlines(), f2.readlines(), fromfile="old.py", tofile="new.py") print("".join(diff))
Output: standard unified diff text block with @@ hunk headers.
Recommendation▾
Show a bad/incorrect usage example (e.g., passing raw multiline string) alongside its fix for contrast
Best Practices
- Use
readlines()(keeps\n) for file diffs sounified_diff/context_diffoutput looks correct; uselineterm=""when lines already lack newlines. - Use
get_close_matches(word, possibilities, n=3, cutoff=0.6)— tunenandcutofffor stricter/looser fuzzy matching. - For large files, prefer
unified_diffovercontext_diff(more compact, standard for patches). - Cache a
SequenceMatcherinstance withset_seq2()when comparing one string against many candidates repeatedly — avoids reprocessing the second sequence each time. - Use
Differ().compare()when you need inline+/-/?markers showing exact character-level changes within modified lines.
Common Pitfalls
- Don't pass raw strings with embedded
\ndirectly tounified_diff/context_diff— split into a list of lines first. - Don't forget diffs are generators — printing the object directly shows a generator repr, not the diff.
- Don't rely on default
autojunk=Truefor short or highly repetitive strings; it can silently produce misleading ratios/matches. ratio()is not symmetric-cost-free — it's O(n×m) in the worst case; avoid calling it in tight loops over large datasets without pre-filtering.get_close_matchesreturns[]silently if nothing meetscutoff— always handle the empty-result case.