Pymorphy — это библиотека для Python, которая выполняет морфологический анализ русских (и, с отдельным словарем, украинских) слов: определяет часть речи и грамматические признаки, приводит слово к нормальной форме (лемме) и ставит его в нужную форму. Опирается на словарь проекта OpenCorpora, а для незнакомых слов строит гипотезы по окончаниям и приставкам.
Содержание
- Pymorphy2 и pymorphy3: какой ставить
- Мини-словарь: что возвращает анализатор
- Минимальный пример: лемматизация предложения
- Морфологический разбор: варианты, score и тег
- Склонение и согласование с числительным
- Незнакомые слова: как pymorphy строит гипотезы
- Типичные ошибки и что делать
- Когда pymorphy подходит, а когда нет
- Выводы
- Где применяется / связь с практикой
- FAQ
Важная граница сразу: pymorphy разбирает одно слово без контекста. Он вернет все возможные варианты разбора и оценит их вероятность, но какой из них верен в конкретном предложении, не знает. Ниже — установка, рабочий пример лемматизации, разбор тегов, склонение и типичные ошибки. Примеры проверены на Python 3.14 и pymorphy3 2.0.6 (сентябрь 2026).
Pymorphy2 и pymorphy3: какой ставить
Под именем «pymorphy» сейчас встречаются три пакета, и их легко перепутать.
| Пакет | Статус | Когда брать |
|---|---|---|
| pymorphy (первая версия) | давно не развивается | не использовать |
| pymorphy2 | последний релиз 0.9.1; на Python 3.11+ не запускается без правок | только поддержка старого кода |
| pymorphy3 | форк pymorphy2, развивается, тот же API | для новых проектов |
API у pymorphy2 и pymorphy3 совпадает: в старом коде обычно достаточно заменить import pymorphy2 на import pymorphy3. Дальше все примеры — на pymorphy3.
python -m pip install pymorphy3
Словарь русского языка (pymorphy3-dicts-ru) ставится автоматически как зависимость. Для украинского нужен отдельный пакет pymorphy3-dicts-uk и явный параметр: pymorphy3.MorphAnalyzer(lang="uk").
Мини-словарь: что возвращает анализатор
Эти термины путают чаще всего, поэтому разведем их до кода.
- Нормальная форма (лемма) — словарная форма слова: для существительного именительный падеж единственного числа, для глагола инфинитив. «сараях» -> «сарай», «ловили» -> «ловить».
- Граммема — один грамматический признак:
NOUN(существительное),gent(родительный падеж),plur(множественное число). - Тег — набор граммем одного разбора, например
VERB,perf,intr plur,past,indc. - Лексема — все формы одного слова: «книга, книги, книге, книгу…».
Лемматизацию не стоит смешивать со стеммингом. Стеммер просто отрезает окончание по правилам и может выдать несуществующую основу. Pymorphy возвращает настоящее словарное слово и умеет обратное — построить форму по признакам.
Минимальный пример: лемматизация предложения
import re
import pymorphy3
morph = pymorphy3.MorphAnalyzer()
text = "Кошки ловили мышей в старых сараях, а собаки спали."
words = re.findall(r"[а-яё]+", text.lower())
lemmas = [morph.parse(w)[0].normal_form for w in words]
print(" ".join(lemmas))
Вывод:
кошка ловить мышь в старый сарай а собака спасть
Почти все верно, но «спали» превратилось в «спасть». Это не баг, а граница метода: словоформа «спали» есть у трех глаголов («спасть», «спалить», «спать»), и у всех трех разборов одинаковый score 0.333. Статистики для этого слова нет, поэтому первый вариант — не самый вероятный, а просто первый по порядку в словаре.
Разбор кода по шагам:
MorphAnalyzer()загружает словарь. Создавайте анализатор один раз на программу, а не внутри цикла.- Регулярное выражение вырезает слова, приводя текст к нижнему регистру.
morph.parse(w)возвращает список вариантов разбора, отсортированный по оценкеscore. Взяли первый..normal_form— лемма этого варианта.
Морфологический разбор: варианты, score и тег
Посмотрим на неоднозначное слово «стали» — это может быть глагол «стать» или существительное «сталь».
import pymorphy3
morph = pymorphy3.MorphAnalyzer()
for p in morph.parse("стали"):
print(p.normal_form, p.tag, round(p.score, 3))
p = morph.parse("стали")[0]
print(p.tag.POS, p.tag.number, p.tag.tense)
print("VERB" in p.tag, {"plur", "past"} in p.tag)
print(p.tag.cyr_repr)
Вывод:
стать VERB,perf,intr plur,past,indc 0.975
сталь NOUN,inan,femn sing,gent 0.011
сталь NOUN,inan,femn plur,nomn 0.005
сталь NOUN,inan,femn sing,datv 0.003
сталь NOUN,inan,femn sing,loct 0.003
сталь NOUN,inan,femn plur,accs 0.003
VERB plur past
True True
ГЛ,сов,неперех мн,прош,изъяв
Что здесь видно:
score— оценка вероятности разбора по статистике корпуса OpenCorpora, а не уверенность в конкретном тексте. В фразе «детали из стали» верен второй вариант, хотя его score около 0.01.- Отдельные признаки читаются через атрибуты тега:
POS,case,number,gender,tenseи другие. Если признака у разбора нет, атрибут вернетNone. - Проверка
"VERB" in p.tagработает и для одной граммемы, и для множества граммем. cyr_reprпоказывает тот же тег русскими сокращениями — удобно для отладки.
Расшифровка тега из примера: VERB — глагол, perf — совершенный вид, intr — непереходный, plur — множественное число, past — прошедшее время, indc — изъявительное наклонение.
Когда нужен конкретный разбор, выбирайте его по признаку, а не по индексу: например, первый вариант с "NOUN" in p.tag, если по задаче вы ищете существительные.
Склонение и согласование с числительным
Метод inflect() ставит слово в форму с нужными граммемами, make_agree_with_number() согласует его с числом, lexeme возвращает все формы.
import pymorphy3
morph = pymorphy3.MorphAnalyzer()
book = morph.parse("книга")[0]
print(book.inflect({"gent"}).word) # нет чего?
print(book.inflect({"plur", "gent"}).word) # много чего?
print(book.inflect({"ablt"}).word) # творительный
for n in (1, 2, 5, 21, 111):
print(n, book.make_agree_with_number(n).word)
print([f.word for f in book.lexeme][:6])
Вывод:
книги
книг
книгой
1 книга
2 книги
5 книг
21 книга
111 книг
['книга', 'книги', 'книге', 'книгу', 'книгой', 'книгою']
Согласование с числом — самый частый практический сценарий: подписи вида «найдено 5 книг» в интерфейсе, письмах и отчетах. Обратите внимание на 111: правило «оканчивается на 1 — единственное число» здесь не работает, и библиотека это учитывает.
Граница метода: он рассчитан на целые неотрицательные числа. Для -1 прогон дает «книг» (нужно «книга»), для дробного 1.5 — тоже «книг», хотя по-русски «1,5 книги». Знак отбрасывайте через abs(n), а для дробных чисел берите родительный падеж единственного числа: book.inflect({"gent"}).word.
Незнакомые слова: как pymorphy строит гипотезы
Слова «бутявка» нет в словаре, но pymorphy все равно его разберет и просклоняет.
import pymorphy3
morph = pymorphy3.MorphAnalyzer()
p = morph.parse("бутявка")[0]
print(p.word, p.tag, p.normal_form, p.score)
print(p.methods_stack)
print(p.inflect({"plur", "gent"}).word)
print(morph.word_is_known("бутявка"), morph.word_is_known("книга"))
Вывод:
бутявка NOUN,inan,femn sing,nomn бутявка 1.0
((DictionaryAnalyzer(), 'явка', 8, 0), (UnknownPrefixAnalyzer(score_multiplier=0.5), 'бут'))
бутявок
False True
methods_stack показывает, как получен разбор: анализатор отделил неизвестную «приставку» «бут» и нашел в словаре «явка». Для новых терминов, фамилий и сленга это полезно, но гипотеза остается гипотезой: word_is_known() помогает отделить словарные слова от предсказанных.
Типичные ошибки и что делать
Ошибка 1. pymorphy2 на новом Python. Код pymorphy2.MorphAnalyzer() на Python 3.14 падает. Результат зависит от окружения: без setuptools — ModuleNotFoundError: No module named 'pkg_resources', с ним — такая ошибка:
AttributeError: module 'inspect' has no attribute 'getargspec'. Did you mean: 'getargs'?
Функцию inspect.getargspec убрали из Python 3.11. Исправление — перейти на pymorphy3 и поменять импорт.
Ошибка 2. inflect() вернул None. Если запрошенной формы у слова нет (например, прошедшее время у существительного), метод возвращает None, а не бросает исключение.
import pymorphy3
morph = pymorphy3.MorphAnalyzer()
p = morph.parse("книга")[0]
form = p.inflect({"past"})
print(form)
print(form.word)
Результат: сначала печатается None, затем падение AttributeError: 'NoneType' object has no attribute 'word'. Исправление — проверять результат:
import pymorphy3
morph = pymorphy3.MorphAnalyzer()
p = morph.parse("книга")[0]
form = p.inflect({"past"})
word = form.word if form is not None else p.word
print(word)
Теперь выводится исходное слово книга.
Ошибка 3. Лемма пишется не так, как в вашем списке. Анализатор понимает слово в любом из двух написаний (с «е» или с «е» с двумя точками), но normal_form возвращает словарный вариант, то есть с двумя точками:
import pymorphy3
morph = pymorphy3.MorphAnalyzer()
for w in ["ежик", "еще"]:
lemma = morph.parse(w)[0].normal_form
print(w, lemma, lemma == w, lemma.replace("ё", "е") == w)
Вывод:
ежик ёжик False True
еще ещё False True
Если вы сравниваете леммы со своим списком, где две точки не ставятся, нормализуйте обе стороны заменой, как в последнем сравнении.
Ошибка 4. Медленная обработка большого текста. Создание анализатора в цикле и повторный разбор одних и тех же слов тратят время зря. Анализатор создавайте один раз, а для частых слов добавьте кеш, например functools.lru_cache на функцию, возвращающую лемму.
Когда pymorphy подходит, а когда нет
| Задача | Pymorphy | Что учесть |
|---|---|---|
| Нормализация слов для поиска, подсчета частот, облака тегов | подходит | ошибки на омонимах вроде «стали», «спали» |
| Склонение и согласование с числом в шаблонах | подходит хорошо | проверять None у inflect() |
| Признаки для классических моделей (TF-IDF и подобные) | подходит | лемматизация уменьшает словарь признаков |
| Точный разбор в контексте предложения | ограниченно | нужны контекстные модели или ручные правила выбора разбора |
| Синтаксис, синонимы, антонимы | не решает | это другие инструменты |
Выводы
- Pymorphy разбирает отдельные русские слова: часть речи, граммемы, нормальная форма, все формы лексемы.
- Для новых проектов берите pymorphy3: pymorphy2 не запускается на Python 3.11+ без правок, API у форка тот же.
parse()возвращает список вариантов по убыванию score; первый вариант — самый частый, но не обязательно верный в вашем предложении.inflect()иmake_agree_with_number()закрывают склонение и подписи вида «5 книг»; результатinflect()проверяйте наNone.- Незнакомые слова разбираются по гипотезам, отличить их помогает
word_is_known().
Где применяется / связь с практикой
Освойте тему на практике
Лемматизация — типичный шаг предобработки русского текста: поиск по сайту, анализ отзывов, подготовка признаков для классификации. В современных NLP-системах часть этой работы берут на себя токенизаторы и трансформерные модели, но понимание морфологии помогает разбирать их ошибки и готовить данные. Системно это изучают на курсе «Языковые трансформенные модели / NLP». Попробовать формат и темы можно на открытых уроках Otus.
FAQ
Нужно ли отдельно скачивать словарь OpenCorpora?
Нет, словарь уже собран в пакет pymorphy3-dicts-ru и ставится вместе с pymorphy3. Скачивать сам корпус нужно, только если вы собираете словарь самостоятельно.
Можно ли держать один MorphAnalyzer на все потоки веб-приложения?
Обычно анализатор создают один раз на процесс и переиспользуют: он только читает словарь. Если нагрузка многопоточная, проверьте поведение на своей версии и под своей нагрузкой.
Работает ли pymorphy с английским языком?
Нет, словари есть для русского и украинского. Для английского используют другие библиотеки лемматизации.



