difflib.md
1> **Источник:** https://python-all.ru/2.7/library/difflib.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 7.4. [`difflib`](https://python-all.ru/2.7/library/difflib.html#module-difflib) – Вспомогательные функции для вычисления различий89Новое в версии 2.1.1011Этот модуль предоставляет классы и функции для сравнения последовательностей. Его можно использовать, например, для сравнения файлов, и он может создавать информацию о различиях в различных форматах, включая HTML, контекстные и объединённые различия. Для сравнения каталогов и файлов см. также модуль [`filecmp`](https://python-all.ru/2.7/library/filecmp.html#module-filecmp).1213#### `class difflib.SequenceMatcher`1415Это гибкий класс для сравнения пар последовательностей любого типа, при условии, что элементы последовательностей являются [хэшируемыми](https://python-all.ru/2.7/glossary.html#term-hashable). Базовый алгоритм предшествует алгоритму, опубликованному в конце 1980-х годов Рэтклиффом и Обершелпом под громким названием «гештальт-сопоставление шаблонов», и немного сложнее его. Идея заключается в поиске самой длинной непрерывной совпадающей подпоследовательности, не содержащей «мусорных» элементов (алгоритм Рэтклиффа и Обершелпа не учитывает мусор). Затем эта же идея рекурсивно применяется к частям последовательностей слева и справа от совпадающей подпоследовательности. Это не даёт минимальных последовательностей редактирования, но обычно даёт совпадения, которые «выглядят правильно» для человека.1617**Время выполнения:** Базовый алгоритм Рэтклиффа-Обершелпа имеет кубическое время в худшем случае и квадратичное время в ожидаемом. [`SequenceMatcher`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher) имеет квадратичное время для худшего случая, а поведение в ожидаемом случае сложным образом зависит от того, сколько элементов у последовательностей общих; время в лучшем случае линейно.1819**Автоматическая эвристика мусора:** [`SequenceMatcher`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher) поддерживает эвристику, которая автоматически помечает определённые элементы последовательности как мусор. Эвристика подсчитывает, сколько раз каждый отдельный элемент появляется в последовательности. Если дубликаты элемента (после первого) составляют более 1% последовательности и длина последовательности не менее 200 элементов, этот элемент помечается как «популярный» и считается мусором для целей сопоставления последовательностей. Эту эвристику можно отключить, установив аргумент `autojunk` в значение `False` при создании [`SequenceMatcher`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher).2021Новое в версии 2.7.1: Параметр *autojunk*.2223#### `class difflib.Differ`2425Это класс для сравнения последовательностей строк текста и создания человекочитаемых различий или дельт. Differ использует [`SequenceMatcher`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher) как для сравнения последовательностей строк, так и для сравнения последовательностей символов внутри похожих (близко совпадающих) строк.2627Каждая строка дельты [`Differ`](https://python-all.ru/2.7/library/difflib.html#difflib.Differ) начинается с двухбуквенного кода:2829| Код | Значение |30| --- | --- |31| `'- '` | строка, уникальная для последовательности 1 |32| `'+ '` | строка, уникальная для последовательности 2 |33| `' '` | строка, общая для обеих последовательностей |34| `'? '` | строка, отсутствующая в каждой входной последовательности |3536Строки, начинающиеся с символа ‘`?`’, призваны помочь глазу заметить различия внутри строк, и отсутствовали в обеих исходных последовательностях. Эти строки могут сбивать с толку, если последовательности содержат символы табуляции.3738#### `class difflib.HtmlDiff`3940Этот класс можно использовать для создания HTML-таблицы (или полного HTML-файла, содержащего таблицу), отображающей поочередное сравнение текста строка за строкой с подсветкой изменений между строками и внутри строк. Таблица может быть создана в режиме полных или контекстных различий.4142Конструктор этого класса:4344#### `__init__(tabsize=8, wrapcolumn=None, linejunk=None, charjunk=IS_CHARACTER_JUNK)`4546Инициализирует экземпляр [`HtmlDiff`](https://python-all.ru/2.7/library/difflib.html#difflib.HtmlDiff).4748*tabsize* – необязательный именованный аргумент для задания шага табуляции; по умолчанию равен `8`.4950*wrapcolumn* – необязательный именованный аргумент, задающий номер столбца, в котором строки разбиваются и переносятся; по умолчанию равен `None`, что означает отсутствие переноса строк.5152*linejunk* и *charjunk* – необязательные именованные аргументы, передаваемые в [`ndiff()`](https://python-all.ru/2.7/library/difflib.html#difflib.ndiff) (используется [`HtmlDiff`](https://python-all.ru/2.7/library/difflib.html#difflib.HtmlDiff) для создания поочередного HTML-сравнения). См. документацию [`ndiff()`](https://python-all.ru/2.7/library/difflib.html#difflib.ndiff) для получения значений по умолчанию и описаний аргументов.5354Следующие методы являются открытыми:5556#### `make_file(fromlines, tolines [, fromdesc][, todesc][, context][, numlines])`5758Сравнивает *fromlines* и *tolines* (списки строк) и возвращает строку, содержащую полный HTML-файл с таблицей, отображающей построчные различия с подсветкой изменений между строками и внутри строк.5960*fromdesc* и *todesc* – необязательные именованные аргументы для задания строк заголовков столбцов исходного и целевого файлов (оба по умолчанию равны пустой строке).6162*context* и *numlines* – необязательные именованные аргументы. Установите *context* в `True`, чтобы показывать контекстные различия; в противном случае по умолчанию используется `False` для отображения полных файлов. *numlines* по умолчанию равен `5`. Когда *context* равен `True`, *numlines* управляет количеством строк контекста, окружающих подсветку различий. Когда *context* равен `False`, *numlines* управляет количеством строк, показываемых перед подсветкой различий при использовании гиперссылок «далее» (установка в ноль приведёт к тому, что гиперссылки «далее» будут размещать следующую подсветку различий в верхней части браузера без предшествующего контекста).6364#### `make_table(fromlines, tolines [, fromdesc][, todesc][, context][, numlines])`6566Сравнивает *fromlines* и *tolines* (списки строк) и возвращает строку, которая представляет собой полную HTML-таблицу с построчным отображением различий, выделяя изменения между строками и внутри строк.6768Аргументы этого метода такие же, как у метода [`make_file()`](https://python-all.ru/2.7/library/difflib.html#difflib.HtmlDiff.make_file) .6970`Tools/scripts/diff.py` – это интерфейс командной строки для данного класса и содержит хороший пример его использования.7172Новое в версии 2.4.7374#### `difflib.context_diff(a, b[, fromfile][, tofile][, fromfiledate][, tofiledate][, n][, lineterm])`7576Сравнивает *a* и *b* (списки строк); возвращает дельту ([генератор](https://python-all.ru/2.7/glossary.html#term-generator), порождающий строки дельты) в формате контекстного diff.7778Контекстные диффы – это компактный способ показать только изменённые строки плюс несколько строк контекста. Изменения отображаются в стиле «до/после». Количество строк контекста задаётся параметром *n*, по умолчанию – три.7980По умолчанию управляющие строки diff (те, что содержат `***` или `---`) создаются с завершающим переводом строки. Это удобно, так как входные данные, созданные с помощью [`file.readlines()`](https://python-all.ru/2.7/library/stdtypes.html#file.readlines), дают diff, подходящие для использования с [`file.writelines()`](https://python-all.ru/2.7/library/stdtypes.html#file.writelines), поскольку и входные, и выходные данные имеют завершающие переводы строк.8182Для входных данных, не имеющих завершающего перевода строки, установите аргумент *lineterm* равным `""`, чтобы выходные данные были полностью без символов новой строки.8384Формат контекстного diff обычно содержит заголовок с именами файлов и временем изменения. Любые или все из них могут быть заданы строками для *fromfile*, *tofile*, *fromfiledate* и *tofiledate*. Время изменения обычно указывается в формате ISO 8601. Если не указано, строки по умолчанию пусты.8586```python87>>> s1 = ['bacon\n', 'eggs\n', 'ham\n', 'guido\n']88>>> s2 = ['python\n', 'eggy\n', 'hamster\n', 'guido\n']89>>> for line in context_diff(s1, s2, fromfile='before.py', tofile='after.py'):90... sys.stdout.write(line) 91*** before.py92--- after.py93***************94*** 1,4 ****95! bacon96! eggs97! ham98 guido99--- 1,4 ----100! python101! eggy102! hamster103 guido104```105106Смотрите [Интерфейс командной строки для difflib](https://python-all.ru/2.7/library/difflib.html#difflib-interface) для более подробного примера.107108Новое в версии 2.3.109110#### `difflib.get_close_matches(word, possibilities[, n][, cutoff])`111112Возвращает список наилучших «достаточно хороших» совпадений. *word* – это последовательность, для которой требуются близкие совпадения (обычно строка), а *possibilities* – список последовательностей, с которыми нужно сопоставлять *word* (обычно список строк).113114Необязательный аргумент *n* (по умолчанию `3`) – максимальное количество близких совпадений, которые нужно вернуть; *n* должно быть больше `0`.115116Необязательный аргумент *cutoff* (по умолчанию `0.6`) – число с плавающей точкой в диапазоне \[0, 1\]. Варианты, которые набирают меньше этого порога сходства с *word*, игнорируются.117118Наилучшие (не более *n*) совпадения из возможных вариантов возвращаются в виде списка, отсортированного по мере сходства: сначала самые похожие.119120```python121>>> get_close_matches('appel', ['ape', 'apple', 'peach', 'puppy'])122['apple', 'ape']123>>> import keyword124>>> get_close_matches('wheel', keyword.kwlist)125['while']126>>> get_close_matches('apple', keyword.kwlist)127[]128>>> get_close_matches('accept', keyword.kwlist)129['except']130```131132#### `difflib.ndiff(a, b[, linejunk][, charjunk])`133134Сравнивает *a* и *b* (списки строк); возвращает дельту в стиле [`Differ`](https://python-all.ru/2.7/library/difflib.html#difflib.Differ) ([генератор](https://python-all.ru/2.7/glossary.html#term-generator), порождающий строки дельты).135136Необязательные именованные параметры *linejunk* и *charjunk* предназначены для функций фильтрации (или `None`):137138*linejunk*: Функция, принимающая один строковый аргумент и возвращающая true, если строка является мусором, и false в противном случае. По умолчанию используется `None`, начиная с Python 2.3. До этого по умолчанию использовалась функция уровня модуля [`IS_LINE_JUNK()`](https://python-all.ru/2.7/library/difflib.html#difflib.IS_LINE_JUNK), которая отсеивает строки без видимых символов, за исключением не более одного символа решётки (`'#'`). Начиная с Python 2.3, базовый класс [`SequenceMatcher`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher) выполняет динамический анализ того, какие строки настолько часты, что представляют собой шум, и это обычно работает лучше, чем значение по умолчанию до версии 2.3.139140*charjunk*: Функция, принимающая символ (строку длины 1) и возвращающая истину, если символ является мусором, и ложь в противном случае. По умолчанию используется функция уровня модуля [`IS_CHARACTER_JUNK()`](https://python-all.ru/2.7/library/difflib.html#difflib.IS_CHARACTER_JUNK), которая отсеивает пробельные символы (пробел или табуляция; замечание: включать в неё символ новой строки – плохая идея!).141142`Tools/scripts/ndiff.py` – это интерфейс командной строки для этой функции.143144```python145>>> diff = ndiff('one\ntwo\nthree\n'.splitlines(1),146... 'ore\ntree\nemu\n'.splitlines(1))147>>> print ''.join(diff),148- one149? ^150+ ore151? ^152- two153- three154? -155+ tree156+ emu157```158159#### `difflib.restore(sequence, which)`160161Возвращает одну из двух последовательностей, породивших дельту.162163По заданной *sequence*, полученной с помощью [`Differ.compare()`](https://python-all.ru/2.7/library/difflib.html#difflib.Differ.compare) или [`ndiff()`](https://python-all.ru/2.7/library/difflib.html#difflib.ndiff), извлекает строки, относящиеся к файлу 1 или 2 (параметр *which*), удаляя префиксы строк.164165Пример:166167```python168>>> diff = ndiff('one\ntwo\nthree\n'.splitlines(1),169... 'ore\ntree\nemu\n'.splitlines(1))170>>> diff = list(diff) # материализовать сгенерированную дельту в список171>>> print ''.join(restore(diff, 1)),172one173two174three175>>> print ''.join(restore(diff, 2)),176ore177tree178emu179```180181#### `difflib.unified_diff(a, b[, fromfile][, tofile][, fromfiledate][, tofiledate][, n][, lineterm])`182183Сравнивает *a* и *b* (списки строк); возвращает дельту ([генератор](https://python-all.ru/2.7/glossary.html#term-generator), порождающий строки дельты) в формате unified diff.184185Унифицированный дифф – это компактный способ показать только изменённые строки плюс несколько строк контекста. Изменения отображаются встроенным стилем (вместо отдельных блоков «до/после»). Количество строк контекста задаётся параметром *n*, который по умолчанию равен трём.186187По умолчанию управляющие строки diff (те, что содержат `---`, `+++` или `@@`) создаются с завершающим переводом строки. Это удобно, так как входные данные, созданные с помощью [`file.readlines()`](https://python-all.ru/2.7/library/stdtypes.html#file.readlines), дают diff, подходящие для использования с [`file.writelines()`](https://python-all.ru/2.7/library/stdtypes.html#file.writelines), поскольку и входные, и выходные данные имеют завершающие переводы строк.188189Для входных данных, не имеющих завершающего перевода строки, установите аргумент *lineterm* равным `""`, чтобы выходные данные были полностью без символов новой строки.190191Формат контекстного diff обычно содержит заголовок с именами файлов и временем изменения. Любые или все из них могут быть заданы строками для *fromfile*, *tofile*, *fromfiledate* и *tofiledate*. Время изменения обычно указывается в формате ISO 8601. Если не указано, строки по умолчанию пусты.192193```python194>>> s1 = ['bacon\n', 'eggs\n', 'ham\n', 'guido\n']195>>> s2 = ['python\n', 'eggy\n', 'hamster\n', 'guido\n']196>>> for line in unified_diff(s1, s2, fromfile='before.py', tofile='after.py'):197... sys.stdout.write(line) 198--- before.py199+++ after.py200@@ -1,4 +1,4 @@201-bacon202-eggs203-ham204+python205+eggy206+hamster207 guido208```209210Смотрите [Интерфейс командной строки для difflib](https://python-all.ru/2.7/library/difflib.html#difflib-interface) для более подробного примера.211212Новое в версии 2.3.213214#### `difflib.IS_LINE_JUNK(line)`215216Возвращает true для игнорируемых строк. Строка *line* игнорируется, если *line* является пустой или содержит один символ `'#'`; в противном случае она не игнорируется. Используется в качестве значения по умолчанию для параметра *linejunk* в [`ndiff()`](https://python-all.ru/2.7/library/difflib.html#difflib.ndiff) до Python 2.3.217218#### `difflib.IS_CHARACTER_JUNK(ch)`219220Возвращает true для игнорируемых символов. Символ *ch* игнорируется, если *ch* является пробелом или табуляцией, в противном случае не игнорируется. Используется как значение по умолчанию для параметра *charjunk* в [`ndiff()`](https://python-all.ru/2.7/library/difflib.html#difflib.ndiff).221222> **См. также**223>224> **[Сопоставление с образцом: гештальт-подход](https://python-all.ru/2.7/library/difflib.html)**225>226> Обсуждение похожего алгоритма Джона У. Рэтклиффа и Д. Э. Мецнера. Оно было опубликовано в [Dr. Dobb’s Journal](https://python-all.ru/2.7/library/difflib.html) в июле 1988 года.227228## 7.4.1. Объекты SequenceMatcher229230Класс [`SequenceMatcher`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher) имеет следующий конструктор:231232#### `class difflib.SequenceMatcher(isjunk=None, a='', b='', autojunk=True)`233234Необязательный аргумент *isjunk* должен быть `None` (значение по умолчанию) или функцией с одним аргументом, которая принимает элемент последовательности и возвращает true, если и только если элемент является «мусором» (junk) и его следует игнорировать. Передача `None` для *isjunk* эквивалентна передаче `lambda x: 0`; иными словами, никакие элементы не игнорируются. Например, передайте:235236```python237lambda x: x in " \t"238```239240если строки сравниваются как последовательности символов и не требуется выравнивание по пробелам или табуляциям.241242Необязательные аргументы *a* и *b* – это последовательности для сравнения; обе по умолчанию равны пустым строкам. Элементы обеих последовательностей должны быть [хэшируемыми](https://python-all.ru/2.7/glossary.html#term-hashable) (hashable).243244Необязательный аргумент *autojunk* можно использовать для отключения автоматической эвристики мусора.245246Новое в версии 2.7.1: Параметр *autojunk*.247248Объекты [`SequenceMatcher`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher) имеют следующие методы:249250#### `set_seqs(a, b)`251252Устанавливает две последовательности для сравнения.253254[`SequenceMatcher`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher) вычисляет и кеширует подробную информацию о второй последовательности, поэтому если требуется сравнить одну последовательность с множеством других, используйте [`set_seq2()`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher.set_seq2) для однократной установки общей последовательности и вызывайте [`set_seq1()`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher.set_seq1) многократно, по одному разу для каждой из остальных последовательностей.255256#### `set_seq1(a)`257258Устанавливает первую последовательность для сравнения. Вторая последовательность для сравнения не изменяется.259260#### `set_seq2(b)`261262Устанавливает вторую последовательность для сравнения. Первая последовательность для сравнения не изменяется.263264#### `find_longest_match(alo, ahi, blo, bhi)`265266Находит самый длинный совпадающий блок в `a[alo:ahi]` и `b[blo:bhi]`.267268Если *isjunk* был опущен или равен `None`, [`find_longest_match()`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher.find_longest_match) возвращает `(i, j, k)` такое, что `a[i:i+k]` равно `b[j:j+k]`, где `alo <= i <= i+k <= ahi` и `blo <= j <= j+k <= bhi`. Для всех `(i', j', k')`, удовлетворяющих этим условиям, дополнительные условия `k >= k'`, `i <= i'`, и если `i == i'`, `j <= j'` также выполняются. Другими словами, среди всех максимальных совпадающих блоков возвращается тот, который начинается раньше всего в *a*, а среди тех максимальных блоков, которые начинаются раньше всего в *a*, возвращается тот, который начинается раньше всего в *b*.269270```python271>>> s = SequenceMatcher(None, " abcd", "abcd abcd")272>>> s.find_longest_match(0, 5, 0, 9)273Match(a=0, b=4, size=5)274```275276Если *isjunk* был указан, сначала определяется самый длинный совпадающий блок, как описано выше, но с дополнительным ограничением: в блоке не должно быть мусорных элементов. Затем этот блок расширяется как можно дальше за счёт сопоставления (только) мусорных элементов с обеих сторон. Таким образом, результирующий блок никогда не совпадает по мусору, за исключением случаев, когда один и тот же мусор оказывается смежным с интересным совпадением.277278Вот тот же пример, что и ранее, но теперь пробелы считаются мусором. Это не позволяет `' abcd'` совпадать с `' abcd'` в хвосте второй последовательности напрямую. Вместо этого может совпасть только `'abcd'`, и он совпадает с самым левым `'abcd'` во второй последовательности:279280```python281>>> s = SequenceMatcher(lambda x: x==" ", " abcd", "abcd abcd")282>>> s.find_longest_match(0, 5, 0, 9)283Match(a=1, b=0, size=4)284```285286Если ни один блок не совпадает, возвращается `(alo, blo, 0)`.287288Изменено в версии 2.6: Этот метод возвращает [именованный кортеж](https://python-all.ru/2.7/glossary.html#term-named-tuple) `Match(a, b, size)`.289290#### `get_matching_blocks()`291292Возвращает список троек, описывающих непересекающиеся совпадающие подпоследовательности. Каждая тройка имеет вид `(i, j, n)` и означает, что `a[i:i+n] == b[j:j+n]`. Тройки монотонно возрастают по *i* и *j*.293294Последняя тройка фиктивная и имеет значение `(len(a), len(b), 0)`. Это единственная тройка с `n == 0`. Если `(i, j, n)` и `(i', j', n')` являются соседними тройками в списке, и вторая не является последней тройкой в списке, то `i+n < i'` или `j+n < j'`; другими словами, соседние тройки всегда описывают несмежные равные блоки.295296Изменено в версии 2.5: Была реализована гарантия того, что соседние тройки всегда описывают несмежные блоки.297298```pycon299>>> s = SequenceMatcher(None, "abxcd", "abcd")300>>> s.get_matching_blocks()301[Match(a=0, b=0, size=2), Match(a=3, b=2, size=2), Match(a=5, b=4, size=0)]302```303304#### `get_opcodes()`305306Возвращает список 5-кортежей, описывающих, как преобразовать *a* в *b*. Каждый кортеж имеет вид `(tag, i1, i2, j1, j2)`. Первый кортеж содержит `i1 == j1 == 0`, а остальные кортежи имеют *i1* равным *i2* из предыдущего кортежа, и, аналогично, *j1* равным предыдущему *j2*.307308Значения *tag* – это строки со следующим смыслом:309310| Значение | Значение |311| --- | --- |312| `'replace'` | `a[i1:i2]` должно быть заменено на `b[j1:j2]`. |313| `'delete'` | `a[i1:i2]` должно быть удалено. Обратите внимание, что `j1 == j2` в этом случае. |314| `'insert'` | `b[j1:j2]` должно быть вставлено в `a[i1:i1]`. Обратите внимание, что `i1 == i2` в этом случае. |315| `'equal'` | `a[i1:i2] == b[j1:j2]` (подпоследовательности равны). |316317Например:318319```python320>>> a = "qabxcd"321>>> b = "abycdf"322>>> s = SequenceMatcher(None, a, b)323>>> for tag, i1, i2, j1, j2 in s.get_opcodes():324... print ("%7s a[%d:%d] (%s) b[%d:%d] (%s)" %325... (tag, i1, i2, a[i1:i2], j1, j2, b[j1:j2]))326 delete a[0:1] (q) b[0:0] ()327 equal a[1:3] (ab) b[0:2] (ab)328replace a[3:4] (x) b[2:3] (y)329 equal a[4:6] (cd) b[3:5] (cd)330 insert a[6:6] () b[5:6] (f)331```332333#### `get_grouped_opcodes([n])`334335Возвращает [генератор](https://python-all.ru/2.7/glossary.html#term-generator) групп с не более чем *n* строками контекста.336337Начиная с групп, возвращаемых [`get_opcodes()`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher.get_opcodes), этот метод выделяет более мелкие кластеры изменений и удаляет промежуточные диапазоны, в которых нет изменений.338339Группы возвращаются в том же формате, что и [`get_opcodes()`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher.get_opcodes).340341Новое в версии 2.3.342343#### `ratio()`344345Возвращает меру схожести последовательностей в виде числа с плавающей запятой в диапазоне \[0, 1\].346347Где T – общее количество элементов в обеих последовательностях, а M – количество совпадений, это 2.0\*M / T. Обратите внимание, что это `1.0`, если последовательности идентичны, и `0.0`, если у них нет ничего общего.348349Это дорого вычислять, если [`get_matching_blocks()`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher.get_matching_blocks) или [`get_opcodes()`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher.get_opcodes) ещё не были вызваны; в этом случае, возможно, стоит сначала попробовать [`quick_ratio()`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher.quick_ratio) или [`real_quick_ratio()`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher.real_quick_ratio), чтобы получить верхнюю границу.350351#### `quick_ratio()`352353Возвращает верхнюю границу для [`ratio()`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher.ratio) относительно быстро.354355#### `real_quick_ratio()`356357Возвращает верхнюю границу для [`ratio()`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher.ratio) очень быстро.358359Три метода, возвращающие отношение совпадающих символов к общему количеству, могут давать разные результаты из-за разного уровня приближения, хотя `quick_ratio()` и `real_quick_ratio()` всегда не меньше, чем `ratio()`:360361```python362>>> s = SequenceMatcher(None, "abcd", "bcde")363>>> s.ratio()3640.75365>>> s.quick_ratio()3660.75367>>> s.real_quick_ratio()3681.0369```370371## 7.4.2. Примеры использования SequenceMatcher372373В этом примере сравниваются две строки, причём пробелы считаются «мусором»:374375```python376>>> s = SequenceMatcher(lambda x: x == " ",377... "private Thread currentThread;",378... "private volatile Thread currentThread;")379```380381`ratio()` возвращает число с плавающей запятой в \[0, 1\], измеряющее схожесть последовательностей. Эмпирически, значение `ratio()` больше 0,6 означает, что последовательности близки к совпадению:382383```python384>>> print round(s.ratio(), 3)3850.866386```387388Если вас интересует только то, где последовательности совпадают, `get_matching_blocks()` удобен:389390```python391>>> for block in s.get_matching_blocks():392... print "a[%d] and b[%d] match for %d elements" % block393a[0] and b[0] match for 8 elements394a[8] and b[17] match for 21 elements395a[29] and b[38] match for 0 elements396```397398Обратите внимание, что последний кортеж, возвращаемый `get_matching_blocks()`, всегда является фиктивным, `(len(a), len(b), 0)`, и это единственный случай, когда последний элемент кортежа (количество совпавших элементов) равен `0`.399400Если вы хотите узнать, как изменить первую последовательность на вторую, используйте `get_opcodes()`:401402```python403>>> for opcode in s.get_opcodes():404... print "%6s a[%d:%d] b[%d:%d]" % opcode405 equal a[0:8] b[0:8]406insert a[8:8] b[8:17]407 equal a[8:29] b[17:38]408```409410> **См. также**411>412> - Функция [`get_close_matches()`](https://python-all.ru/2.7/library/difflib.html#difflib.get_close_matches) в этом модуле показывает, как простой код, построенный на [`SequenceMatcher`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher), может использоваться для полезной работы.413> - [Простой рецепт контроля версий](https://python-all.ru/2.7/library/difflib.html) для небольшого приложения, построенного с помощью [`SequenceMatcher`](https://python-all.ru/2.7/library/difflib.html#difflib.SequenceMatcher).414415## 7.4.3. Объекты Differ416417Обратите внимание, что дельты, сгенерированные [`Differ`](https://python-all.ru/2.7/library/difflib.html#difflib.Differ), не претендуют на то, чтобы быть **минимальными** различиями. Напротив, минимальные различия часто противоречат интуиции, поскольку они синхронизируются везде, где возможно, иногда случайно сопоставляя фрагменты, отстоящие на 100 страниц. Ограничение точек синхронизации смежными совпадениями сохраняет некоторое понятие локальности, ценой иногда более длинного diff.418419Класс [`Differ`](https://python-all.ru/2.7/library/difflib.html#difflib.Differ) имеет следующий конструктор:420421#### `class difflib.Differ([linejunk[, charjunk]])`422423Необязательные именованные параметры *linejunk* и *charjunk* предназначены для функций фильтрации (или `None`):424425*linejunk*: функция, которая принимает один строковый аргумент и возвращает истину, если строка является мусорной. По умолчанию – `None`, то есть ни одна строка не считается мусорной.426427*charjunk*: функция, которая принимает один символьный аргумент (строку длиной 1) и возвращает истину, если символ является мусорным. По умолчанию – `None`, то есть ни один символ не считается мусорным.428429Объекты [`Differ`](https://python-all.ru/2.7/library/difflib.html#difflib.Differ) используются (генерируют дельты) через единственный метод:430431#### `compare(a, b)`432433Сравнивает две последовательности строк и генерирует дельту (последовательность строк).434435Каждая последовательность должна содержать отдельные однострочные строки, заканчивающиеся символами новой строки. Такие последовательности можно получить из метода [`readlines()`](https://python-all.ru/2.7/library/stdtypes.html#file.readlines) файлоподобных объектов. Сгенерированная дельта также состоит из строк, завершающихся новой строкой, готовых к печати как есть с помощью метода [`writelines()`](https://python-all.ru/2.7/library/stdtypes.html#file.writelines) файлоподобного объекта.436437## 7.4.4. Пример Differ438439Этот пример сравнивает два текста. Сначала мы задаем тексты – последовательности отдельных однострочных строк, заканчивающихся символами новой строки (такие последовательности также можно получить из метода [`readlines()`](https://python-all.ru/2.7/library/stdtypes.html#file.readlines) файлоподобных объектов):440441```python442>>> text1 = ''' 1. Beautiful is better than ugly.443... 2. Explicit is better than implicit.444... 3. Simple is better than complex.445... 4. Complex is better than complicated.446... '''.splitlines(1)447>>> len(text1)4484449>>> text1[0][-1]450'\n'451>>> text2 = ''' 1. Beautiful is better than ugly.452... 3. Simple is better than complex.453... 4. Complicated is better than complex.454... 5. Flat is better than nested.455... '''.splitlines(1)456```457458Затем мы создаем экземпляр объекта Differ:459460```python461>>> d = Differ()462```463464Обратите внимание, что при создании экземпляра объекта [`Differ`](https://python-all.ru/2.7/library/difflib.html#difflib.Differ) можно передать функции для фильтрации «мусорных» строк и символов. Подробнее см. конструктор [`Differ()`](https://python-all.ru/2.7/library/difflib.html#difflib.Differ).465466Наконец, мы сравниваем два:467468```python469>>> result = list(d.compare(text1, text2))470```471472`result` – это список строк, поэтому давайте выведем его в удобочитаемом виде:473474```python475>>> from pprint import pprint476>>> pprint(result)477[' 1. Beautiful is better than ugly.\n',478 '- 2. Explicit is better than implicit.\n',479 '- 3. Simple is better than complex.\n',480 '+ 3. Simple is better than complex.\n',481 '? ++\n',482 '- 4. Complex is better than complicated.\n',483 '? ^ ---- ^\n',484 '+ 4. Complicated is better than complex.\n',485 '? ++++ ^ ^\n',486 '+ 5. Flat is better than nested.\n']487```488489В виде одной многострочной строки это выглядит так:490491```python492>>> import sys493>>> sys.stdout.writelines(result)494 1. Beautiful is better than ugly.495- 2. Explicit is better than implicit.496- 3. Simple is better than complex.497+ 3. Simple is better than complex.498? ++499- 4. Complex is better than complicated.500? ^ ---- ^501+ 4. Complicated is better than complex.502? ++++ ^ ^503+ 5. Flat is better than nested.504```505506## 7.4.5. Интерфейс командной строки для difflib507508В этом примере показано, как использовать difflib для создания утилиты, подобной `diff`. Он также включён в дистрибутив исходного кода Python, как `Tools/scripts/diff.py`.509510```python511""" Интерфейс командной строки для difflib.py, выводящий разницу в четырёх форматах:512513* ndiff: выводит каждую строку и подсвечивает изменения между строками.514* context: подсвечивает кластеры изменений в формате «до/после».515* unified: подсвечивает кластеры изменений во встроенном формате.516* html: генерирует построчное сравнение с подсветкой изменений.517518"""519520import sys, os, time, difflib, optparse521522def main():523 # Настройка парсера аргументов524 usage = "usage: %prog [options] fromfile tofile"525 parser = optparse.OptionParser(usage)526 parser.add_option("-c", action="store_true", default=False,527 help='Produce a context format diff (default)')528 parser.add_option("-u", action="store_true", default=False,529 help='Produce a unified format diff')530 hlp = 'Produce HTML side by side diff (can use -c and -l in conjunction)'531 parser.add_option("-m", action="store_true", default=False, help=hlp)532 parser.add_option("-n", action="store_true", default=False,533 help='Produce a ndiff format diff')534 parser.add_option("-l", "--lines", type="int", default=3,535 help='Set number of context lines (default 3)')536 (options, args) = parser.parse_args()537538 if len(args) == 0:539 parser.print_help()540 sys.exit(1)541 if len(args) != 2:542 parser.error("need to specify both a fromfile and tofile")543544 n = options.lines545 fromfile, tofile = args # в соответствии со строкой использования546547 # передаются в качестве аргументов функции diff548 fromdate = time.ctime(os.stat(fromfile).st_mtime)549 todate = time.ctime(os.stat(tofile).st_mtime)550 with open(fromfile, 'U') as f:551 fromlines = f.readlines()552 with open(tofile, 'U') as f:553 tolines = f.readlines()554555 if options.u:556 diff = difflib.unified_diff(fromlines, tolines, fromfile, tofile,557 fromdate, todate, n=n)558 elif options.n:559 diff = difflib.ndiff(fromlines, tolines)560 elif options.m:561 diff = difflib.HtmlDiff().make_file(fromlines, tolines, fromfile,562 tofile, context=options.c,563 numlines=n)564 else:565 diff = difflib.context_diff(fromlines, tolines, fromfile, tofile,566 fromdate, todate, n=n)567568 # используется writelines, так как diff – генератор569 sys.stdout.writelines(diff)570571if __name__ == '__main__':572 main()573```574