You forgot the first parameter to SequenceMatcher.

>>> import difflib
>>> 
>>> a='abcd'
>>> b='ab123'
>>> seq=difflib.SequenceMatcher(None, a,b)
>>> d=seq.ratio()*100
>>> print d
44.4444444444

http://docs.python.org/library/difflib.html

Answer from Lennart Regebro on Stack Overflow
🌐
Python
docs.python.org › 3 › library › difflib.html
difflib — Helpers for computing deltas
Source code: Lib/difflib.py This module provides classes and functions for comparing sequences. It can be used for example, for comparing files, and can produce information about file differences i...
🌐
Educative
educative.io › answers › what-is-sequencematcher-in-python
What is SequenceMatcher() in Python?
SequenceMatcher is a class that is available in the difflib Python package.
🌐
TutorialsPoint
tutorialspoint.com › article › sequencematcher-in-python-for-longest-common-substring
SequenceMatcher in Python for Longest Common Substring.
The SequenceMatcher class is the part of the Python difflib module. It is used to compare sequence (such as lists or strings) and finds the similarities between them. The task is to find the Longest Common Substring, i.e, the longest sequence of the
🌐
TestDriven.io
testdriven.io › tips › 6de2820b-785d-4fc1-b107-ed8215528f49
Tips and Tricks - Python - Using SequenceMatcher.ratio() to find similarity between two strings | TestDriven.io
Python tip: You can use difflib.SequenceMatcher.ratio() to get the distance between two strings: T - total number of elements in both strings (len(first_string) + len(second_string)) M - number of matches ·
🌐
Beautiful Soup
tedboy.github.io › python_stdlib › generated › generated › difflib.SequenceMatcher.html
difflib.SequenceMatcher — Python Standard Library
See the Differ class for a fancy human-friendly file differencer, which uses SequenceMatcher both to compare sequences of lines, and to compare sequences of characters within similar (near-matching) lines.
🌐
HexDocs
hexdocs.pm › difflib › Difflib.SequenceMatcher.html
Difflib.SequenceMatcher — Difflib v0.1.0
SequenceMatcher tries to compute a "human-friendly diff" between two sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the longest contiguous & junk-free matching subsequence.
🌐
GeeksforGeeks
geeksforgeeks.org › sequencematcher-in-python-for-longest-common-substring
SequenceMatcher in Python for Longest Common Substring - GeeksforGeeks
March 24, 2023 - # Function to find Longest Common Sub-string from difflib import SequenceMatcher def longestSubstring(str1,str2): # initialize SequenceMatcher object with # input string seqMatch = SequenceMatcher(None,str1,str2) # find match of longest sub-string # output will be like Match(a=0, b=0, size=5) match = seqMatch.find_longest_match(0, len(str1), 0, len(str2)) # print longest substring if (match.size!=0): print (str1[(match.a: match.a + match.size)]) else: print ('No longest common sub-string found') # Driver program if __name__ == "__main__": str1 = 'GeeksforGeeks' str2 = 'GeeksQuiz' longestSubstring(str1,str2)
Find elsewhere
Top answer
1 of 2
27

SequenceMatcher.ratio internally uses SequenceMatcher.get_matching_blocks to calculate the ratio, I will walk you through the steps to see how that happens:

SequenceMatcher.get_matching_blocks

Return list of triples describing matching subsequences. Each triple is of the form (i, j, n), and means that a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in i and j.

The last triple is a dummy, and has the value (len(a), len(b), 0). It is the only triple with n == 0. If (i, j, n) and (i', j', n') are adjacent triples in the list, and the second is not the last triple in the list, then i+n != i' or j+n != j'; in other words, adjacent triples always describe non-adjacent equal blocks.

ratio internally uses SequenceMatcher.get_matching_blocks 's results, and sums the sizes of all matched sequences returned bySequenceMatcher.get_matching_blocks. This is the exact source code from difflib.py:

matches = sum(triple[-1] for triple in self.get_matching_blocks())

The above line is critical, because the result of the above expression is used to compute the ratio. We'll see that shortly and how it impacts the calculation of the ratio.


>>> m1 = SequenceMatcher(None, "Ebojfm Mzpm", "Ebfo ef Mfpo")
>>> m2 = SequenceMatcher(None, "Ebfo ef Mfpo", "Ebojfm Mzpm")

>>> matches1 = sum(triple[-1] for triple in m1.get_matching_blocks())
>>> matches1
7
>>> matches2 = sum(triple[-1] for triple in m2.get_matching_blocks())
>>> matches2
6

As you can see, we have 7 and 6. These are simply the sums of the matched subsequences as returned by get_matching_blocks. Why does this matter? Here's why, the ratio is computed in the following way, (this is from difflib source code):

def _calculate_ratio(matches, length):
    if length:
        return 2.0 * matches / length
    return 1.0

length is len(a) + len(b) where a is the first sequence and b being the second sequence.

Okay, enough talk, we need actions:

>>> length = len("Ebojfm Mzpm") + len("Ebfo ef Mfpo") 
>>> m1.ratio()
0.6086956521739131
>>> (2.0 * matches1 / length)  == m1.ratio()
True

Similarly for m2:

>>> 2.0 * matches2 / length
0.5217391304347826 
>>> (2.0 * matches2 / length) == m2.ratio()
True

Note: Not all SequenceMatcher(None a,b).ratio() == SequenceMatcher(None b,a).ratio() are False, sometimes they can be True:

>>> s1 = SequenceMatcher(None, "abcd", "bcde").ratio()
>>> s2 = SequenceMatcher(None, "bcde", "abcd").ratio()
>>> s1 == s2
True

In case you're wondering why, this is because

sum(triple[-1] for triple in self.get_matching_blocks())

is the same for both SequenceMatcher(None, "abcd", "bcde") and SequenceMatcher(None, "bcde", "abcd") which is 3.

2 of 2
11

My answer does not provide exact details of the observed problem, but contains a general explanation of why such things may happen with loosely defined diffing methods.

Essentially everything boils down to the fact that, in the general case,

  1. more than one common subsequences of the same length can be extracted from a given pair of strings, and

  2. longer common subsequences may appear less natural to a human expert than a shorter one.

Since you are puzzled by this particular case let's analyze common subsequence identification on the following pair of strings:

  • my stackoverflow mysteries
  • mystery

To me, the natural match is "MYSTER", as follows:

my stackoverflow MYSTERies
.................MYSTERy..

However, the longest match fully covers the shorter of the two strings as follows:

MY STackovERflow mYsteries
MY.ST.....ER......Y.......

The drawback of such a match is that it introduces multiple matching sub-blocks whereas the (shorter) natural match is contiguous.

Therefore, diffing algorithms are tweaked so that their outputs are more pleasing to the final user. As a result, they are not 100% mathematically elegant and therefore don't possess properties that you would expect from a purely academic (rather than practical) tool.

The documentation of SequenceMatcher contains a corresponding note:

class difflib.SequenceMatcher

This is a flexible class for comparing pairs of sequences of any type, so long as the sequence elements are hashable. The basic algorithm predates, and is a little fancier than, an algorithm published in the late 1980’s by Ratcliff and Obershelp under the hyperbolic name “gestalt pattern matching.” The idea is to find the longest contiguous matching subsequence that contains no “junk” elements (the Ratcliff and Obershelp algorithm doesn’t address junk). The same idea is then applied recursively to the pieces of the sequences to the left and to the right of the matching subsequence. This does not yield minimal edit sequences, but does tend to yield matches that “look right” to people.

🌐
CodeSpeedy
codespeedy.com › home › sequencematcher in python
SequenceMatcher in Python - CodeSpeedy
February 9, 2020 - #import the class from difflib import SequenceMatcher s1 = "gun" s2 = "run" sequence = SequenceMatcher(a=s1 , b=s2) #comparing both the strings print(sequence.ratio())
🌐
SourceForge
epydoc.sourceforge.net › stdlib › difflib.SequenceMatcher-class.html
difflib.SequenceMatcher - Epydoc - SourceForge
The same idea is then applied ... sequences, but does tend to yield matches that "look right" to people. SequenceMatcher tries to compute a "human-friendly diff" between two sequences....
🌐
Python
docs.python.org › 2.4 › lib › sequencematcher-examples.html
4.4.2 SequenceMatcher Examples
October 18, 2006 - >>> s = SequenceMatcher(lambda x: x == " ", ... "private Thread currentThread;", ...
🌐
Python
docs.python.org › 2.4 › lib › sequence-matcher.html
4.4.1 SequenceMatcher Objects
October 18, 2006 - SequenceMatcher computes and caches detailed information about the second sequence, so if you want to compare one sequence against many sequences, use set_seq2() to set the commonly used sequence once and call set_seq1() repeatedly, once for each of the other sequences.
🌐
GeeksforGeeks
geeksforgeeks.org › compare-sequences-in-python-using-dfflib-module
Compare sequences in Python using dfflib module - GeeksforGeeks
February 24, 2021 - Python3 · # import required module import difflib # assign parameters par1 = ['g', 'f', 'g'] par2 = 'gfg' # compare print(difflib.SequenceMatcher(None, par1, par2).ratio()) Output: 1.0 · Example 2: Python3 · # import required module import difflib # assign parameters par1 = 'Geeks for geeks!' par2 = 'geeks' # compare print(difflib.SequenceMatcher(None, par1, par2).ratio()) Output: 0.47619047619047616 ·
🌐
7-Zip Documentation
documentation.help › Python-2.5 › sequence-matcher.html
4.4.1 SequenceMatcher Objects - Python 2.5 Documentation
SequenceMatcher computes and caches detailed information about the second sequence, so if you want to compare one sequence against many sequences, use set_seq2() to set the commonly used sequence once and call set_seq1() repeatedly, once for each of the other sequences.
🌐
Medium
medium.com › @zhangkd5 › a-tutorial-for-difflib-a-powerful-python-standard-library-to-compare-textual-sequences-096d52b4c843
A Tutorial of Difflib — A Powerful Python Standard Library to Compare Textual Sequences | by Kaidong Zhang | Medium
January 27, 2024 - In this tutorial, we learned and practiced the difflib Python standard library, and explored its powerful capability to compare text sequences. Whether it is to compare versions of files or to find the similarity between strings, difflib can ...
🌐
PyPI
pypi.org › project › cdifflib
cdifflib · PyPI
from cdifflib import CSequenceMatcher import difflib difflib.SequenceMatcher = CSequenceMatcher import library_that_uses_difflib # Now the library will transparantely be using the C SequenceMatcher - other # things remain the same library_that_uses_difflib.do_some_diffing()
      » pip install cdifflib
    
Published   Jan 13, 2025
Version   1.2.9
🌐
Amanxai
amanxai.com › home › all articles › sequencematcher in python
SequenceMatcher in Python - AmanXai by Aman Kharwal
March 3, 2022 - text1 = "My Name is Aman Kharwal" text2 = "I am the founder of thecleverprogrammer.com" sequenceScore = SequenceMatcher(None, text1, text2).ratio() print(f"Both are {sequenceScore * 100} % similar") ... So, according to the score above, it shows that both the text inputs have less similar sequences. This is how you can use this class in Python available in the difflib module.
🌐
Runebook.dev
runebook.dev › en › docs › python › library › difflib › difflib.SequenceMatcher
python - SequenceMatcher Explained: Word-Level Diffs, Junk Handling, and Fuzzy Matching
import difflib text_a = "Hello, World!" text_b = "Hello World" # No comma or exclamation point # Define a function to treat space, comma, and exclamation mark as junk def is_punctuation_junk(char): return char in ' ,!' # Compare, ignoring punctuation sm_junk = difflib.SequenceMatcher(is_punctuation_junk, text_a, text_b) print(f"Ratio ignoring punctuation: {sm_junk.ratio():.3f}") # Output: Ratio ignoring punctuation: 1.000 (Because 'Hello' and 'World' are a perfect match)