Найдите существительную фразу до и после числа в тексте

Aug 19 2020

Учитывая текст, я должен найти предыдущие слова для всех чисел до стоп-слова, принадлежащего списку check_words (вид стоп-слов).

Мой код:

check_words = ['the', 'a', 'with','to']
mystring = 'the code to find the beautiful words 78 that i have to nicely check 45 with the snippet'
list_of_words = mystring.split()

В этом конкретном тексте я бы проверил раньше '78'и '45'вернусь назад до точки, где я найду любое из слов в check_words (но не более 8 слов).

Код для этого может быть:

preceding_chunks = []
for i,word in enumerate(list_of_words):
    if any(char.isdigit() for char in word):
       
        # 8 precedent words (taking into account that I can not slice with 8 back at the beginning)
        preceding_words = list_of_words[max(0,i-8):i]
        preceding_words[::-1]

        # I check from the end of the list towards the start
        for j,sub_word in enumerate(preceding_words[::-1]):
            if  sub_word in check_words:
                # printing out j for checking
                myposition = j
                print(j)
                real_preceding_chunk = preceding_words[len(preceding_words)-j:]
                print(real_preceding_chunk)
                preceding_chunks.append(real_preceding_chunk)
                break

Этот код работает. в основном я проверяю каждое слово, но у меня сложилось впечатление (возможно, я ошибаюсь), что этого можно добиться с помощью пары одинарных вкладышей и, следовательно, без петель. Есть идеи?


ПРИМЕЧАНИЕ. Этот вопрос касается улучшения читаемости кода, попыток избавиться от циклов, чтобы сделать код быстрее, и попыток сделать код лучше, что является частью дзен Python.


ПРИМЕЧАНИЕ 2: Некоторые предыдущие проверки, которые я сделал:

  • Поиск позиции элемента в другом списке из числа в другом списке
  • Поиск индекса элемента в списке
  • Найти в списке

Ответы

MarioIshac Aug 19 2020 at 09:13

Я придумал это:

import itertools
import re

chunks = (grouped_chunk.split() for grouped_chunk in re.split("\\s+\\d+\\s+", mystring))
preceding_chunks = []

for reversed_chunk in map(reversed, chunks):
    preceding_chunk = list(itertools.takewhile(lambda word: word not in check_words, reversed_chunk))[::-1]
    preceding_chunks.append(preceding_chunk)

Мы применяем itertools.takewhileк, reversed_chunkкоторый дает нам предыдущий кусок в обратном порядке. Затем мы получаем правильно упорядоченный preceding_chunk, переставляя в конце с [::-1].

Регулярное выражение разбивается mystringна основе числа (экранированного \d+). Окружающие экранированные символы \s+s представляют собой любые отступы вокруг числа. Это приводит к тому, что этот код ведет себя иначе, чем ваш, если цифры и буквы смешиваются в одних и тех же словах (например, a1).

Для вашего исходного кода я бы сделал пару предложений:

  1. Следуйте PEP 8 . Например, добавьте интервал после запятой в i,word.
  2. Удалите лишнее выражение preceding_words[::-1]. Хотя это оценивается как обратное preceding_words, потому что оно не на месте, оценка не имеет побочных эффектов. Кроме того, вы уже выполняете этот разворот в enumerate(preceding_words[::-1]).