确定生成器产生的值的数量

时间:2013-09-24 10:58:14

标签: python python-3.x

假设我有这些代码:

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产生的值的数量?

3 个答案:

答案 0 :(得分: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

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


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

调用此方法来获取下一个项目。self.count必须在调用next之后添加,因为我们允许StopIteration传播而不想添加如果我们没有产生价值,那就算了。


def __iter__(self):
    return self

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

答案 1 :(得分: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

答案 2 :(得分:0)

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

words = list(words_generator(text))

另一种选择是使用itertools.tee

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

之后您可以使用iterable的两个副本。但请注意,如果您首先完全遍历副本,那么只需构建列表就会更快,内存效率更高。要在内存方面看到任何增益,你应该以某种方式“同时”迭代这两个副本。 例如:

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)个内存,其中n是文本中的字数,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)内存(如果生成器是常量空间)。

但请注意,使用显式循环会降低代码速度,因此最后,如果内存不是一个选项,那么为list构建words可能是更好的解决方案。