SequenceMatcher.ratio
内部使用SequenceMatcher.get_matching_blocks
来计算比率,我将引导您完成这些步骤,看看它是如何发生的:
SequenceMatcher.get_matching_blocks
返回描述匹配子序列的三元组列表。每个三元组都具有这种形式(i, j, n)
,并且意味着a[i:i+n] == b[j:j+n]
。i
三元组在和中单调递增j
。
最后一个三元组是一个哑元,并且具有值(len(a), len(b), 0)
。它是唯一的三元组n == 0
。If (i, j, n)
和(i', j', n')
是列表中相邻的三元组,第二个不是列表中的最后一个三元组,那么i+n != i'
或j+n != j'
; 换句话说,相邻的三元组总是描述不相邻的相等块。
ratio
内部使用SequenceMatcher.get_matching_blocks
的结果,并将 . 返回的所有匹配序列的大小相加SequenceMatcher.get_matching_blocks
。这是来自的确切源代码difflib.py
:
matches = sum(triple[-1] for triple in self.get_matching_blocks())
上面的行很关键,因为上面表达式的结果用于计算比率。我们很快就会看到它以及它如何影响比率的计算。
>>> 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
如您所见,我们有 7 和 6。这些只是由 . 返回的匹配子序列的总和get_matching_blocks
。为什么这很重要?这就是为什么,比率是按以下方式计算的(这来自difflib
源代码):
def _calculate_ratio(matches, length):
if length:
return 2.0 * matches / length
return 1.0
length
是len(a) + len(b)
哪里a
是第一个序列并且b
是第二个序列。
好的,说得够多了,我们需要行动:
>>> length = len("Ebojfm Mzpm") + len("Ebfo ef Mfpo")
>>> m1.ratio()
0.6086956521739131
>>> (2.0 * matches1 / length) == m1.ratio()
True
同样对于m2
:
>>> 2.0 * matches2 / length
0.5217391304347826
>>> (2.0 * matches2 / length) == m2.ratio()
True
注意:并非所有 SequenceMatcher(None a,b).ratio() == SequenceMatcher(None b,a).ratio()
都是False
,有时它们可以是True
:
>>> s1 = SequenceMatcher(None, "abcd", "bcde").ratio()
>>> s2 = SequenceMatcher(None, "bcde", "abcd").ratio()
>>> s1 == s2
True
如果你想知道为什么,这是因为
sum(triple[-1] for triple in self.get_matching_blocks())
两者都相同SequenceMatcher(None, "abcd", "bcde")
,SequenceMatcher(None, "bcde", "abcd")
即3。