def inflect(morph, lastname, gram_form): ''' Вернуть вариант фамилии который соотвествует данной грамматической форме Параметры: * morph - объект Morph * lastname - фамилия которую хотим склонять * gram_form - желаемые характеристики грам. формы (если u'жр' отсутствует в этом параметре, то по-умолчанию принимается u'мр', или u'мр-жр', если указано u'мн') ''' expected_form = GramForm(gram_form) gender_tag = (u'мр-жр' if expected_form.match_string(u'мн') else None) if not gender_tag: gender_tag = (expected_form.match_string(u'жр') or u'мр') # За один проход проверяется, что исходное слово может быть склонено как # фамилия и выбирается форма подходящая под gram_form present_in_decline = False accepted = {} for item in decline(lastname): form = GramForm(item.get('info', u'')) # Если в результате склонения не получилось исходной формы - ложное срабатывание # Обязательно проверяется род: при склонении в противоположном роде # может получиться исходная форма но нас интересует совпадение только в # заданном роде if item.get('word', '') == lastname: # В случае склонения во множественную форму, род игнорируется. # Род всех фамилий во множественном числе - мр-жр. if expected_form.match_string(u'мн') or form.match_string(gender_tag): present_in_decline = True expected = form.match(expected_form) if expected and not accepted: accepted = item # Здесь break не нужен т.к. present_in_decline всё ещё может быть # не установлена в корректное значение # Если в результате склонения исходной формы не получилось, # возвращается результат склонения как для обычного слова if present_in_decline and accepted: return accepted.get('word', u'') else: return morph.inflect_ru(lastname, gram_form, smart_guess=False)
def is_correct(frm): correct = True if norm: correct = frm['norm'] == norm if method: correct = correct and (method in frm['method']) if cls: correct = correct and (frm['class'] == cls) if form: gram_filter = GramForm(form) gram_form = GramForm(frm['info']) correct = correct and gram_form.match(gram_filter) return correct
def test_match_inverted(self): form = GramForm("мр,ед,имя") self.assertFalse(form.match(GramForm("мр,!имя"))) self.assertTrue(form.match(GramForm("ед,!тв")))
def test_match(self): form = GramForm("мр,ед,имя") self.assertTrue(form.match(GramForm("мр"))) self.assertTrue(form.match(GramForm("ед,мр")))
def decline(lastname, gram_form=u''): ''' Склоняет фамилию и возвращает все возможные формы ''' # Из фамилии выделяется предполагаемая лемма (Табуретов -> Табуретов, # Табуретовым -> Табуретов), лемма склоняется по правилам склонения фамилий def guess_lemma(name): ''' Попытаться угадать сложносклоняемую фамилию (Цапок, Бегунец, Берия) Возвращает пару (name=lemma+suffix, lemma) либо (None, None) ''' name_len = len(name) # Попытка угадать склонённую фамилию из 13.1.12 ("Берией") if name_len > 2 and name[-2:] in (u'ИИ', u'ИЮ',): return (lastname[:-2] + u'ИЯ', lastname[:-2]) elif name_len > 3 and name[-3:] in (u'ИЕЙ',): return (lastname[:-3] + u'ИЯ', lastname[:-3]) # Попытка угадать склонённую фамилию, закачивающуюся на -ок ("Цапка") # Работает, только если буква перед окончанием согласная. # Проверка согласной делается для исключения склонённых фамилий на -ак # ("Собчака") if name_len > 3 and name[-2:] in (u'КА', u'КУ', u'КЕ',) and name[-3] in CONSONANTS: return (lastname[:-2] + u'ОК', lastname[:-2]) elif name_len > 4 and name[-3:] in (u'КОМ',) and name[-4] in CONSONANTS: return (lastname[:-3] + u'ОК', lastname[:-3]) # Попытка угадать склонённую фамилию, закачивающуюся на -ец ("Бегунец") # FIXME: необходима проверка на коллизии с другими фамилиями (как в # случае с "Цапок") if name_len > 3 and name[-2:] in (u'ЦА', u'ЦУ', u'ЦЕ',): return (lastname[:-2] + u'ЕЦ', lastname[:-2]) return (None, None) match = LASTNAME_PATTERN.search(lastname) lemma = name = match.group(1) if match else lastname # name is lemma + suffix name_len = len(name) guessed_name, guessed_lemma = guess_lemma(name) if guessed_name and guessed_lemma: name, lemma = guessed_name, guessed_lemma cases, plural_cases = {}, () if name_len > 2: cases, plural_cases = CASEMAP.get(name[-2:], ({}, ())) if cases: lemma = name[:-2] if not cases and name_len > 3: cases, plural_cases = CASEMAP.get(name[-3:], ({}, ())) if cases: lemma = name[:-3] # В случае 13.1.12 лемма состоит из фамилии, за исключением # двух последних букв if cases is CASES_IA or cases is CASES_OK: lemma = name = name[:-2] if not cases: return [] expected_form = GramForm(gram_form) forms = [] for i, case in zip(xrange(6), (u'им', u'рд', u'дт', u'вн', u'тв', u'пр',)): for gender_tag in (u'мр', u'жр',): form = GramForm(u'%s,%s,фам,ед' % (case, gender_tag,)) if gram_form and not form.match(expected_form): continue forms.append({ 'word': u'%s%s' % (name, cases[gender_tag][i]), 'class': u'С', 'info': form.get_form_string(), 'lemma': name, 'method': u'decline_lastname (%s)' % lastname, 'norm': u'%s%s' % (name, cases[gender_tag][0]), }) plural_form = GramForm(u'%s,мр-жр,фам,мн' % (case,)) if gram_form and not plural_form.match(expected_form): continue forms.append({ 'word': u'%s%s' % (name, plural_cases[i]), 'class': u'С', 'info': plural_form.get_form_string(), 'lemma': name, 'method': u'decline_lastname (%s)' % lastname, 'norm': u'%s%s' % (name, plural_cases[0]), }) # Просклонять рекурсивно для случая с множественным числом фамилии. # Козловых -> фам,им; Козловых (мн) -> Козлов -> фам,им if lemma != name and LASTNAME_PATTERN.match(lemma): refinement = decline(lemma) if refinement: return forms + refinement return forms
def test_match_inverted(self): form = GramForm(u"мр,ед,имя") self.assertFalse(form.match(GramForm(u"мр,!имя"))) self.assertTrue(form.match(GramForm(u"ед,!тв")))
def test_match(self): form = GramForm(u"мр,ед,имя") self.assertTrue(form.match(GramForm(u"мр"))) self.assertTrue(form.match(GramForm(u"ед,мр")))