1

假设我有以下代码:

counter = Counter()
text = f.read()
words = words_generator(text)
interesting_words = filter_generator(words)
counter.update(interesting_words)

for i in counter:
    print("Frequency for "+i ": "+counter[i]/sum)

我应该如何最好地设置sum哪个值是由 产生的值的数量words_generator

4

3 回答 3

4
from collections import Counter

class CountItemsWrapper:
    def __init__(self, items):
        self.items = iter(items)
        self.count = 0

    def __next__(self):
        res = next(self.items)
        self.count += 1
        return res

    def __iter__(self):
        return self

counter = Counter()
text = f.read()
words = CountItemsWrapper(words_generator(text))
interesting_words = filter_generator(words)
counter.update(interesting_words)

for i in counter:
    print("Frequency for "+i ": "+counter[i]/words.count)

基本上,CountItemsWrapper它是一个只传递值的迭代器,但只要它传递就会保持计数。

然后,您可以count将包装器上的属性用作您的sum.


类的解释:

def __init__(self, items):
    self.items = iter(items)
    self.count = 0

这很简单。请记住,实例是迭代器,而不仅仅是迭代器。所以这个迭代一次,保持一次计数。


def __next__(self):
    res = next(self.items)
    self.count += 1
    return res

这被称为获取下一个项目。self.count必须在调用next添加,因为我们允许 StopIteration 传播,并且如果我们没有产生值,则不想添加到计数中。


def __iter__(self):
    return self

这是一个迭代器,所以它返回自己。

于 2013-09-24T11:17:02.117 回答
2

Q&D 可能的技术解决方案:将您的生成器包装到一个迭代中,以跟踪所见项目的数量,即:

class IterCount(object):
    def __init__(self, iterable):
        self._iterable = iterable
        self._count = 0

    def _itercount(self):
        for value in self._iterable:
            self._count += 1
            yield value

    def __iter__(self):
        return self._itercount()

    @property
    def count(self):
        return self._count


itc1 = IterCount(range(10))
print list(itc1)
print itc1.count

itc2 = IterCount(xrange(10))
print list(itc2)
print itc2.count
于 2013-09-24T11:22:23.640 回答
0

最简单的解决方案是建立一个列表:

words = list(words_generator(text))

另一种选择是使用itertools.tee

words, words_copy = itertools.tee(words_generator(text))

之后,您可以使用可迭代的两个副本。但是请注意,如果您首先完全迭代一个副本,那么简单地构建列表将会更快,内存效率更高。要查看内存方面的任何增益,您应该以某种方式“同时”迭代两个副本。例如:

filtered = filter_generator(words)
total = 0
for word, _ in zip(filtered, words_copy): # use itertools.izip in python2
    counter[word] += 1
    total += 1
total += sum(1 for _ in words_copy)

最多使用O(n-k)内存 wheren是文本中的单词数,是文本k中有趣的单词数。您可以使用以下方法简化代码:

from itertools import zip_longest #izip_longest in python2
filtered = filter_generator(words)
total = 0
for word, _ in zip_longest(filtered, words_copy):
    counter[word] += 1
    total += 1
del counter[None]

仅使用O(1)内存(如果生成器是常量空间)。

但是请注意,具有显式循环会减慢代码速度,因此最后,如果内存不是一个选项,构建一个listforwords可能是更好的解决方案。

于 2013-09-24T11:24:56.470 回答