执行计数,排序/映射大型骰子

时间:2014-12-07 05:19:32

标签: python sorting dictionary key-value counting

我在Reddit本周做的是'easy'Daily Programmer Challenge。描述在链接上,但基本上挑战是从URL读取文本文件并进行字数统计。不用说,结果输出是一个相当大的字典对象。我有几个问题,主要是关于根据价值访问或排序密钥。

首先,我根据我目前对OOP和优秀Python风格的理解开发了代码。我希望它尽可能健壮,但我也想使用最少量的导入模块。我的目标是成为一名优秀的程序员,因此我认为有必要建立一个强大的基础,并尽可能地找出自己做事的方法。话虽这么说,代码:

from urllib2 import urlopen

class Word(object):

    def __init__(self):
        self.word_count = {}    

    def alpha_only(self, word):
        """Converts word to lowercase and removes any non-alphabetic characters."""
        x = ''
        for letter in word:
            s = letter.lower()
            if s in 'abcdefghijklmnopqrstuvwxyz':
                x += s
        if len(x) > 0:
            return x    

    def count(self, line):
        """Takes a line from the file and builds a list of lowercased words containing only alphabetic chars.
            Adds each word to word_count if not already present, if present increases the count by 1."""
        words = [self.alpha_only(x) for x in line.split(' ') if self.alpha_only(x) != None]
        for word in words:
            if word in self.word_count:
                self.word_count[word] += 1
            elif word != None:
                self.word_count[word] = 1

class File(object):

    def __init__(self,book):
        self.book = urlopen(book)               
        self.word = Word()

    def strip_line(self,line):
        """Strips newlines, tabs, and return characters from beginning and end of line. If remaining string > 1,
            splits up the line and passes it along to the count method of the word object."""
        s = line.strip('\n\r\t')
        if s > 1:
            self.word.count(s)

    def process_book(self):
        """Main processing loop, will not begin processing until the first line after the line containing "START".
            After processing it will close the file."""
        begin = False
        for line in self.book:
            if begin == True:
                self.strip_line(line)
            elif 'START' in line:
                begin = True
        self.book.close()

book = File('http://www.gutenberg.org/cache/epub/47498/pg47498.txt')

book.process_book()

count = book.word.word_count

所以现在我有一个相当准确和健壮的字数,可能没有任何重复或空白条目,但仍然是一个包含超过3k键/值对的字典对象。我不能使用for k,v in count迭代它,或者它给我异常ValueError: too many values to unpack,它排除了使用列表理解或映射到函数来执行任何类型的排序。

我正在读这个HowTo on Sorting并在几分钟前玩它并注意到for x in count.items()让我遍历一个键/值对列表而不会抛出ValueError异常,所以我删除了这一行count = book.word.word_count并添加了以下内容:

s_count = sorted(book.word.word_count.items(), key=lambda count: count[1], reverse=True)

# Delete the original dict, it is no longer needed
del book.word.word_count

现在我终于有一个排序的单词列表s_count。唷!所以,我的问题是:

  1. dict是否是执行原始计数的最佳数据类型? count.items()返回的元组列表是否更可取?但这可能会减慢速度,对吗?

  2. 这似乎有点'笨重',因为我正在构建一个字典,将其转换为包含元组的列表,然后对列表进行排序并返回一个新列表。但是,据我所知,字典允许我执行最快的查找,所以我在这里遗漏了一些东西吗?

  3. 我简要阅读了关于哈希的内容。虽然我认为我理解的重点是散列将节省内存空间并允许我执行更快的查找和比较,但是不会因为程序变得更加计算成本(更高的CPU负载)而导致折衷然后计算每个单词的哈希值?哈希是否与此相关?

  4. 任何有关命名约定的反馈(我很擅长),或者基本上任何其他建议(包括样式)的任何其他建议都将不胜感激。

1 个答案:

答案 0 :(得分:2)

您确定for k,v in count:是否给出例外ValueError: too many values to unpack?我希望它能给ValueError: need more than 1 value to unpack

当您使用dict作为迭代器时(例如在for循环中),您只需获取密钥,您就无法获取值。如果您需要键值对,则需要使用注释中无花果所提到的dict iteritems()方法(或在Python 3中使用items()方法)。

当然,您可以随时执行以下操作:

for k in count:
    print k, count[k]

...

我认为您的大部分问题更适合Code Review而不是Stack Overflow。但既然你在这里问得这么好,我会提到几点。 :)

通过char构建字符串char效率相当低,因此如果在列表中收集字符然后使用alpha_only()方法将它们加入到str.join()方法中,则count()方法会更好单个字符串。通常的Python惯用法会使用列表理解来做到这一点。

alpha_only()方法中的列表理解为每个单词调用strip()两次,效率很高。

您可以使用默认参数使split()调用更简单,因为它会删除所有空格(并且您不需要在此应用程序中保留空格字符)。类似地,使用alpha_only()及其默认arg将在任何空白空间中分割,这在此应用程序中可能更好,因为给出单个空格的arg意味着您将获得一些空字符串。如果一行中有多个空格的运行,则由split返回的列表。

...

您在问题中提到散列,以及它是否对此应用程序有用。是的。 Python词典实际上使用其键的散列,因此您不必担心细节。是的,字典是用于此任务的良好数据结构。有更高级的字典形式使事情变得更简单,但使用它们确实需要导入(标准)模块。但是使用字典(某种或某种字体)来保存数据然后从中生成元组列表以进行最终排序是Python中相当普遍的做法。如果该程序即将终止,则无需专门删除字典。

...

至于for的重复呼叫,每当你发现自己做了那种事情时,表明列表理解并不适合这项任务,你应该只使用它一个普通的words = [] for word in line.split(): word = self.alpha_only(word) if word is not None: words.append(word) 循环,这样你就可以保存函数调用的结果而不必重新计算它。例如,

{{1}}