我正在从一本介绍性的Python教科书中学习python,并且我遇到了以下问题:
您将实现函数index(),该函数将文本文件的名称和单词列表作为输入。对于列表中的每个单词,您的函数将在文本文件中找到单词出现的行并打印相应的行号。
前:
>>>> index('raven.txt', ['raven', 'mortal', 'dying', 'ghost', 'ghastly', 'evil', 'demon'])
ghost 9
dying 9
demon 122
evil 99, 106
ghastly 82
mortal 30
raven 44, 53, 55, 64, 78, 97, 104, 111, 118, 120
以下是我对此问题的尝试:
def index(filename, lst):
infile = open(filename, 'r')
lines = infile.readlines()
lst = []
dic = {}
for line in lines:
words = line.split()
lst. append(words)
for i in range(len(lst)):
for j in range(len(lst[i])):
if lst[i][j] in lst:
dic[lst[i][j]] = i
return dic
当我运行该函数时,我得到一个空字典。我不明白为什么我得到一本空字典。那我的功能有什么问题?谢谢。
答案 0 :(得分:1)
试试这个,
def index(filename, lst):
dic = {w:[] for w in lst}
for n,line in enumerate( open(filename,'r') ):
for word in lst:
if word in line.split(' '):
dic[word].append(n+1)
return dic
这里介绍的语言有一些你应该注意的功能,因为从长远来看,它们会让生活变得更轻松。
首先是词典理解。它基本上使用lst
中的单词作为键来初始化字典,并使用空列表[]
作为每个键的值。
接下来是enumerate
命令。这允许我们迭代序列中的项目,但也给我们这些项目的索引。在这种情况下,因为我们将文件对象传递给enumerate
,它将循环遍历这些行。对于每次迭代,n
将是该行的从0开始的索引,line
将是该行本身。接下来,我们迭代lst
中的单词。
请注意,我们这里不需要任何索引。 Python鼓励循环遍历序列中的对象,而不是循环遍历索引,然后基于索引访问序列中的对象(例如,不鼓励执行for i in range(len(lst)): do something with lst[i])
。
最后,in
运算符是一种非常简单的方法来测试许多类型对象的成员资格,语法非常直观。在这种情况下,我们要求的是当前lst
中来自line
的当前单词。
请注意,我们使用line.split(' ')
来获取该行中单词的列表。如果我们不这样做,'the' in 'there was a ghost'
将返回True
,因为the
是其中一个字词的子字符串。
另一方面,'the' in ['there', 'was', 'a', 'ghost']
将返回False
。如果条件返回True
,我们将它附加到与字典中的键相关联的列表中。
这可能有很多东西可以咀嚼,但这些概念使得这样的问题更加直截了当。
答案 1 :(得分:1)
您正在覆盖lst
的值。您将它用作函数的参数(在这种情况下,它是字符串列表)和文件中的单词列表(在这种情况下,它是字符串列表的列表)。当你这样做时:
if lst[i][j] in lst
比较始终返回False
,因为lst[i][j]
是str
,但lst
仅包含字符串列表,而不包含字符串本身。这意味着永远不会执行dic
的分配,结果会得到一个空的dict
。
为避免这种情况,您应该为存储单词的列表使用不同的名称,例如:
In [4]: !echo 'a b c\nd e f' > test.txt
In [5]: def index(filename, lst):
...: infile = open(filename, 'r')
...: lines = infile.readlines()
...: words = []
...: dic = {}
...: for line in lines:
...: line_words = line.split()
...: words.append(line_words)
...: for i in range(len(words)):
...: for j in range(len(words[i])):
...: if words[i][j] in lst:
...: dic[words[i][j]] = i
...: return dic
...:
In [6]: index('test.txt', ['a', 'b', 'c'])
Out[6]: {'a': 0, 'c': 0, 'b': 0}
还有很多事情你可以改变。
如果要迭代列表,则不必显式使用索引。如果您需要索引,可以使用enumerate
:
for i, line_words in enumerate(words):
for word in line_words:
if word in lst: dict[word] = i
您也可以直接在文件上进行迭代(有关更多信息,请参阅python教程的Reading and Writing Files部分):
# use the with statement to make sure that the file gets closed
with open('test.txt') as infile:
for i, line in enumerate(infile):
print('Line {}: {}'.format(i, line))
事实上,我不明白你为什么要先建立words
列表清单。在构建字典时直接迭代文件:
def index(filename, lst):
with open(filename, 'r') as infile:
dic = {}
for i, line in enumerate(infile):
for word in line.split():
if word in lst:
dic[word] = i
return dic
您的dic
值应该是列表,因为多行可以包含相同的单词。就目前而言,dic
只存储找到单词的最后一行:
from collections import defaultdict
def index(filename, words):
# make faster the in check afterwards
words = frozenset(words)
with open(filename) as infile:
dic = defaultdict(list)
for i, line in enumerate(infile):
for word in line.split():
if word in words:
dic[word].append(i)
return dic
如果您不想使用collections.defaultdict
,可以将dic = defaultdict(list)
替换为dic = {}
,然后更改:
dic[word].append(i)
使用:
if word in dic:
dic[word] = [i]
else:
dic[word].append(i)
或者,您也可以使用dict.setdefault
:
dic.setdefault(word, []).append(i)
虽然最后一种方法比原始代码慢一点。
请注意,所有这些解决方案都具有以下属性:如果在文件中找不到单词,则它根本不会出现在结果中。但是,您可能希望在结果中使用emty列表作为值。在这种情况下,在开始循环之前使用空列表的dict
更简单,例如:
dic = {word : [] for word in words}
for i, line in enumerate(infile):
for word in line.split():
if word in words:
dic[word].append(i)
请参阅有关List Comprehensions和Dictionaries的文档以了解第一行。
你也可以迭代words
而不是行,如下所示:
dic = {word : [] for word in words}
for i, line in enumerate(infile):
for word in words:
if word in line.split():
dic[word].append(i)
但请注意,这会更慢,因为:
line.split()
会返回一个列表,因此word in line.split()
必须扫描所有列表。line.split()
。你可以尝试解决这两个问题:
dic = {word : [] for word in words}
for i, line in enumerate(infile):
line_words = frozenset(line.split())
for word in words:
if word in line_words:
dic[word].append(i)
请注意,我们在line.split()
上迭代一次以构建集合,并且还在words
上。根据两组的大小,这可能比原始版本更慢或更快(iteratinv超过line.split()
)。
然而,此时它可能更快地与集合相交:
dic = {word : [] for word in words}
for i, line in enumerate(infile):
line_words = frozenset(line.split())
for word in words & line_words: # & stands for set intersection
dic[word].append(i)
答案 2 :(得分:0)
首先,带有单词的函数参数名为lst
,并且将所有单词放在文件中的列表也命名为lst
,因此您不会保存传递给您的单词函数,因为在第4行你要重新声明列表。
其次,您正在迭代文件中的每一行(第一个for
),并获取该行中的单词。之后lst
包含整个文件中的所有单词。因此,在for i ...
中,您正在迭代从文件中获取的所有单词,因此无需使用第三个for j
来迭代每个单词中的每个字符。
在简历中,if
你说“如果这个单个字符在单词列表中...... ”哪个不是,那么dict将永远不会被填满
for i in range(len(lst)):
if words[i] in lst:
dic[words[i]] = dic[words[i]] + i # To count repetitions
你需要重新思考这个问题,即使我的答案会失败,因为dict中的单词不会存在错误,但你明白了。祝你好运!