Python улучшает производительность при поиске строки в файле

Я пытаюсь оптимизировать производительность скрипта Python, который ищет, содержится ли строка (имя класса) в файле. Файл содержит около 100 000 строк, каждая строка с именем класса.

def readFile(fileName):
    fileObj = open(fileName, "r")  # opens the file in read mode
    words = []
    lines = fileObj.read().splitlines()
    # Using a for loop to check if word should be qualified as a class
    # (Not included here for brevity)
    for line in lines:        
        line = line.strip()
        words.extend(line.split())

    fileObj.close()
    return words

#Actual array contains about 1000 classes to check
words[] = ["class1","class2","class3"]

# Contains about 100.000 lines
classes = readFile('classes.txt')
for word in words:
   if (word in classes):
   # do something

При запуске приведенного выше кода для завершения требуется около 60 секунд (включая некоторую конкатенацию строк, которая, я думаю, не должна быть узким местом). Есть ли более быстрый способ поиска строки в файле или просто это невозможно, и только БД может сделать лучше?

Что такое readFile? Пожалуйста, предоставьте минимальный воспроизводимый пример. Вероятно, проблема в том, что readFile возвращает список слов. Вместо этого используйте set. Проверка 1000 слов в set слов должна быть практически мгновенной.

juanpa.arrivillaga 15.12.2020 10:50

«включая некоторую конкатенацию строк, которая, я думаю, не должна быть узким местом», как именно вы выполняете конкатенацию строк?

juanpa.arrivillaga 15.12.2020 10:51

Интересно, может быть более эффективно проверять, когда вы читаете файл, а не записывать его в список, а затем проверять по этому списку.

JeffUK 15.12.2020 10:53

пиздец, все это не имеет значения. Если classes — это контейнер на основе хэша, set, то это не займет много времени. Это явно не так, почти наверняка list

juanpa.arrivillaga 15.12.2020 10:57

Что-то вроде for line in open('classes.txt'): if any(word in line for word in words): должно быть достаточно хорошо...

Tomerikoo 15.12.2020 10:57

@Tomerikoo нет. это все еще полиномиальное время.

juanpa.arrivillaga 15.12.2020 10:58
lines = fileObj.read().splitlines() это не способ сделать это в Python. Просто используйте for line in fileObj: ...
juanpa.arrivillaga 15.12.2020 11:00

Я действительно извиняюсь за отсутствие определения readFile. Только что добавил.

Francesco Marchioni 15.12.2020 11:00
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
8
95
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Просто сделайте это:

classes = set(readFile('classes.txt'))

(Или еще лучше, просто верните set из вашей функции).

Тогда это больше не должно занимать 60 секунд, это должно быть практически мгновенно для 1000 слов.

Проблема в том, что вы проверяете:

word in classes

Но поскольку classes — это список, это операция с линейным временем, поэтому в конечном итоге вы получаете полиномиальное время. Когда classes является набором, это будет постоянное время.

Вот пример использования случайных строк:

In [1]: import string

In [2]: import random

In [3]: words = [''.join(random.sample(string.ascii_lowercase, 10)) for _ in range(1000)]

In [4]: classes = [''.join(random.sample(string.ascii_lowercase, 10)) for _ in range(100_000)]

In [5]: %%timeit
   ...: for word in words:
   ...:     word in classes
   ...:
1.22 s ± 6.48 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Вы также смутно упомянули что-то о конкатенации строк. Вы также можете ввести поведение полиномиального времени, если сделаете это неправильно. Но вы этого не показали.

@Tomerikoo, конечно, суть все та же.

juanpa.arrivillaga 15.12.2020 11:00

Зависит от того, есть ли несколько экземпляров одного и того же слова, что может иметь значение в других задачах, чем представленная.

hetepeperfan 15.12.2020 11:01

спасибо за классный совет. Не могу поверить, что это только что увеличилось с 60 секунд до 1 секунды только с использованием set(). Спасибо!

Francesco Marchioni 15.12.2020 11:09

Другие вопросы по теме