2014-07-24 51 views
0

我从介绍性的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 

当我运行的功能,我回来一个空的字典。我不明白我为什么得到一本空字典。那么我的功能有什么问题?谢谢。

+2

您将在'lst = []'行中将'[]'重新赋值给给定的参数'lst'。这只是一个错字。 – soon

+2

说实话,这个逻辑在很多方面都是错误的。我建议你首先重新审视你试图实现的算法。 – NPE

回答

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 

您也可以直接迭代上的一个文件(参阅更多的信息蟒蛇教程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列表。只是itertate直接在构建字典文件:

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 = {}替代dic = defaultdict(list),然后改变:

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 ComprehensionsDictionaries,了解第一线。然而

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) 

注意,这将是慢,因为:

您还可以重复words的替代线路,这样

  • 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。根据两组的大小,这可能比原始版本更慢或更快(迭代超过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) 
+0

非常感谢您的详细回复。回顾一下这个问题,我忘记了我已经把这个函数作为输入。所以,我有点不好意思问这样一个简单的问题。尽管如此,感谢您的回复。我从中学到了很多东西。 – Quino

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(' ')

注获得的该行中的单词的列表。如果我们不't do this,'the' in 'there was a ghost' will return True as the是其中一个单词的子串。另一方面'the' in ['there', 'was', 'a', 'ghost']将返回False。如果条件返回True,我们将它附加到与我们的字典中的键相关联的列表中。

这可能是很多咀嚼,但这些概念使这样的问题更直接。

0

首先,用的话你的函数参数是名为lst也是,你把所有的单词的列表中的文件也被命名lst,所以你没有保存传递给你的函数的单词,因为在第4行中,你正在重新声明列表。

其次,您正在遍历文件中的每一行(第一个for),并获取该行中的单词。之后,lst包含整个文件中的所有单词。因此,在for i ...中,您正在遍历文件中的所有单词,因此无需使用第三个for j,您可以在每个单词中遍历每个字符。

在简历中,那if你说的是“如果这个单个字符在单词列表中......”这个不是,所以字典永远不会填满。

for i in range(len(lst)): 
    if words[i] in lst: 
    dic[words[i]] = dic[words[i]] + i # To count repetitions 

你需要重新思考的问题,甚至因为在字典中的字将不存在给了一个错误我的答案会失败,但你明白了吧。祝你好运!

相关问题