假设我有这些代码:
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
产生的值的数量?
答案 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
可能是更好的解决方案。