Вот встроенное отличие, вдохновленное ответом @ tzot выше (также совместимым с Python 3)
def inline_diff(a, b):
import difflib
matcher = difflib.SequenceMatcher(None, a, b)
def process_tag(tag, i1, i2, j1, j2):
if tag == 'replace':
return '{' + matcher.a[i1:i2] + ' -> ' + matcher.b[j1:j2] + '}'
if tag == 'delete':
return '{- ' + matcher.a[i1:i2] + '}'
if tag == 'equal':
return matcher.a[i1:i2]
if tag == 'insert':
return '{+ ' + matcher.b[j1:j2] + '}'
assert false, "Unknown tag %r"%tag
return ''.join(process_tag(*t) for t in matcher.get_opcodes())
Это не идеально - например, было бы неплохо расширить коды операций замены, чтобы распознавать замененное полное слово вместо нескольких разных букв, но это хорошее место для начала.
Пример вывода:
>>> a='Lorem ipsum dolor sit amet consectetur adipiscing'
>>> b='Lorem bananas ipsum cabbage sit amet adipiscing'
>>> print(inline_diff(a, b))
Lorem{+ bananas} ipsum {dolor -> cabbage} sit amet{- consectetur} adipiscing