我在Python 3.6中编写了一些代码来自动完成用户输入的(部分)单词。我的目标是尽可能快速/高效地运行自动完成部分,作为一种开发我的编程技巧和更好地理解Python的方法。我仅限于使用标准库,因为我想探索使用这样的constaint可以实现的目标。
代码的第一部分是一个函数def load_wordfile
,它加载一个.txt并创建一个单词列表,在整个过程中小写和清理单词。我试图通过列表理解和string.punctuation
加快速度,尽管这不是我的主要关注点。
import string
from collections import Counter
def load_wordfile(filename):
with open(filename, encoding='utf8') as f:
# List comprehension and string.punctuation for efficiency and speed improvements
return [word.strip(string.punctuation) for line in f for word in line.lower().split()]
然后在实例化期间将单词列表传递给类AutoCompleter
。在这个类中,方法complete
实际上执行自动完成。我希望自动完成的结果作为元组列表,其中元组中的第一个值是匹配的单词,第二个值是单词出现的相对频率(在.txt中)。返回的元组列表应按频率降序排列。我知道Counter
对此有好处,所以我已经使用它了。
class AutoCompleter:
def __init__(self, word_list):
# Counter takes care of word counts and descending order
self.counts = Counter(word_list)
# Total word count for relative frequency calculation
total = float(len(word_list))
# Change the counts to frequencies
for k, v in self.counts.items():
self.counts[k] = v/total
# Turn Counter dict into a list in descending order
self.counts = self.counts.most_common()
def complete(self, pattern):
# Clean user entered word
pattern = pattern.lower().strip(string.punctuation)
# This is the potential bottleneck
completions = [pair for pair in self.counts if pair[0].startswith(pattern)]
return completions
在执行速度方面,我设法改进了我的第一次尝试,这导致了您在上面看到的代码。
但是,我相信complete
仍然可以改进。为了证明我现在的位置,我在NLTK英语单词语料库上对complete
进行了基准测试,发现这是一个很好的“上限”,需要搜索匹配的列表大小。
from nltk.corpus import words
# words.words() is a list of over 250,000 unique English words
cmp = AutoCompleter(words.words())
pattern = 'do'
%timeit -n 100 cmp.complete(pattern)
来自timeit的结果是:
41.6 ms ± 898 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
通过广泛的研究,我认为我可以使用某种二分搜索或bisect
来加速搜索与用户输入相匹配的(部分)单词。或者可能以不同的方式将输入结构化为complete
,找到匹配单词的索引,然后从不同的列表中拉出频率,最后将两者一起输出。
我也有一个想法,那就是用户键入“a”然后我应该只搜索以“a”开头的单词而不是传递列表中的每个单词。但是现在我对如何最好地实现这一点感到困惑。我也看过Stack Overflow,这有助于其他方面,但我还没有完全把所有的部分组合在一起,以便最终提升性能。
非常感谢您的帮助和建议。谢谢!