有什么办法可以让这台发电机干涸吗?
Is there any way to dry out this generator?
以下方法在我的 class 中,并尝试在完成工作之前自行准备。作为其后的处理循环,引物懒惰地执行其工作。在这两个循环中重复了五行,我不清楚消除重复的最佳方法是什么。
@classmethod
def __get_start_words(cls, iterable, n, start_words):
iterator, buffer, sentinel = iter(iterable), Deque(maxlen=n), object()
for _ in range(n):
item = next(iterator, sentinel)
if item is sentinel:
# raise ValueError('iterable was too short to satisfy n')
break
buffer.append(item)
yield item
start_words[buffer.prefix] += 1
while True:
if buffer[0][-1] in cls.TERMINATORS:
start_words[buffer.suffix] += 1
item = next(iterator, sentinel)
if item is sentinel:
break
buffer.append(item)
yield item
在 class 或方法中,是否有一种有效且清晰的方法只写一次最后五行?
附录
在回答关于 prefix
和 suffix
是什么的问题时,这里是 Deque
class:
class Deque(collections.deque):
"""Deque([iterable[, maxlen]]) -> Deque instance"""
@property
def prefix(self):
"""Property allowing capture of all but last item in deque."""
item = self.pop()
value = tuple(self)
self.append(item)
return value
@property
def suffix(self):
"""Property allowing capture of all but first item in deque."""
item = self.popleft()
value = tuple(self)
self.appendleft(item)
return value
第二个版本
考虑到别人说的,为了效率写了下面的方法:
@classmethod
def __get_start_words(cls, iterable, n, start_words):
iterator, buffer, count = iter(iterable), Deque(maxlen=n), 0
for item, count in zip(iterator, range(n)):
buffer.append(item)
yield item
if count + 1 < n:
raise ValueError('iterable was too short to satisfy n')
start_words[buffer.prefix] += 1
try:
while True:
if buffer[0][-1] in cls.TERMINATORS:
start_words[buffer.suffix] += 1
item = next(iterator)
buffer.append(item)
yield item
except StopIteration:
pass
第三版
该方法的第三个版本改编自 Daniel 富有洞察力的回答:
@classmethod
def __get_start_words(cls, iterable, n, start_words):
count, buffer = 0, Deque(maxlen=n)
for count, item in enumerate(iterable, 1):
yield item
buffer.append(item)
if count == n:
start_words[buffer.prefix] += 1
if count >= n and buffer[0][-1] in cls.TERMINATORS:
start_words[buffer.suffix] += 1
if count < n:
raise ValueError('iterable was too short to satisfy n')
最终版本
此方法明显优于我的第一个版本 -- 感谢在这里帮助我的人。
@classmethod
def __get_start_words(cls, iterable, n, start_words):
buffer = Deque(maxlen=n)
for count, item in enumerate(iterable, 1):
yield item
buffer.append(item)
if count == n:
start_words[buffer.prefix] += 1
if count >= n and buffer[0][-1] in cls.TERMINATORS:
start_words[buffer.suffix] += 1
if len(buffer) < n:
raise ValueError('iterable was too short to satisfy n')
使用for
循环:
@classmethod
def __get_start_words(cls, iterable, n, start_words):
buffer = Deque(maxlen=n)
for idx, item in enumerate(iterable, 1):
buffer.append(item)
yield item
if idx == n:
start_words[buffer.prefix] += 1
if idx >= n and buffer[0][-1] in cls.TERMINATORS:
start_words[buffer.suffix] += 1
if len(buffer) < n:
raise ValueError('iterable was too short to satisfy n')
关于您的第二个版本的一些想法:使用 islice
时不需要 count
:
for item in islice(iterator, n):
buffer.append(item)
yield item
if len(buffer) < n:
raise ValueError('iterable was too short to satisfy n')
进一步重构导致:
@classmethod
def __get_start_words(cls, iterable, n, start_words):
iterable = iter(iterable)
buffer = deque(islice(iterable, n-1))
yield from buffer
if len(buffer) < n - 1:
raise ValueError('iterable was too short to satisfy n')
start_words[tuple(buffer)] += 1
for item in iterable:
buffer.append(item)
yield item
first = buffer.popleft()
if first[-1] in cls.TERMINATORS:
start_words[tuple(buffer)] += 1
以下方法在我的 class 中,并尝试在完成工作之前自行准备。作为其后的处理循环,引物懒惰地执行其工作。在这两个循环中重复了五行,我不清楚消除重复的最佳方法是什么。
@classmethod
def __get_start_words(cls, iterable, n, start_words):
iterator, buffer, sentinel = iter(iterable), Deque(maxlen=n), object()
for _ in range(n):
item = next(iterator, sentinel)
if item is sentinel:
# raise ValueError('iterable was too short to satisfy n')
break
buffer.append(item)
yield item
start_words[buffer.prefix] += 1
while True:
if buffer[0][-1] in cls.TERMINATORS:
start_words[buffer.suffix] += 1
item = next(iterator, sentinel)
if item is sentinel:
break
buffer.append(item)
yield item
在 class 或方法中,是否有一种有效且清晰的方法只写一次最后五行?
附录
在回答关于 prefix
和 suffix
是什么的问题时,这里是 Deque
class:
class Deque(collections.deque):
"""Deque([iterable[, maxlen]]) -> Deque instance"""
@property
def prefix(self):
"""Property allowing capture of all but last item in deque."""
item = self.pop()
value = tuple(self)
self.append(item)
return value
@property
def suffix(self):
"""Property allowing capture of all but first item in deque."""
item = self.popleft()
value = tuple(self)
self.appendleft(item)
return value
第二个版本
考虑到别人说的,为了效率写了下面的方法:
@classmethod
def __get_start_words(cls, iterable, n, start_words):
iterator, buffer, count = iter(iterable), Deque(maxlen=n), 0
for item, count in zip(iterator, range(n)):
buffer.append(item)
yield item
if count + 1 < n:
raise ValueError('iterable was too short to satisfy n')
start_words[buffer.prefix] += 1
try:
while True:
if buffer[0][-1] in cls.TERMINATORS:
start_words[buffer.suffix] += 1
item = next(iterator)
buffer.append(item)
yield item
except StopIteration:
pass
第三版
该方法的第三个版本改编自 Daniel 富有洞察力的回答:
@classmethod
def __get_start_words(cls, iterable, n, start_words):
count, buffer = 0, Deque(maxlen=n)
for count, item in enumerate(iterable, 1):
yield item
buffer.append(item)
if count == n:
start_words[buffer.prefix] += 1
if count >= n and buffer[0][-1] in cls.TERMINATORS:
start_words[buffer.suffix] += 1
if count < n:
raise ValueError('iterable was too short to satisfy n')
最终版本
此方法明显优于我的第一个版本 -- 感谢在这里帮助我的人。
@classmethod
def __get_start_words(cls, iterable, n, start_words):
buffer = Deque(maxlen=n)
for count, item in enumerate(iterable, 1):
yield item
buffer.append(item)
if count == n:
start_words[buffer.prefix] += 1
if count >= n and buffer[0][-1] in cls.TERMINATORS:
start_words[buffer.suffix] += 1
if len(buffer) < n:
raise ValueError('iterable was too short to satisfy n')
使用for
循环:
@classmethod
def __get_start_words(cls, iterable, n, start_words):
buffer = Deque(maxlen=n)
for idx, item in enumerate(iterable, 1):
buffer.append(item)
yield item
if idx == n:
start_words[buffer.prefix] += 1
if idx >= n and buffer[0][-1] in cls.TERMINATORS:
start_words[buffer.suffix] += 1
if len(buffer) < n:
raise ValueError('iterable was too short to satisfy n')
关于您的第二个版本的一些想法:使用 islice
时不需要 count
:
for item in islice(iterator, n):
buffer.append(item)
yield item
if len(buffer) < n:
raise ValueError('iterable was too short to satisfy n')
进一步重构导致:
@classmethod
def __get_start_words(cls, iterable, n, start_words):
iterable = iter(iterable)
buffer = deque(islice(iterable, n-1))
yield from buffer
if len(buffer) < n - 1:
raise ValueError('iterable was too short to satisfy n')
start_words[tuple(buffer)] += 1
for item in iterable:
buffer.append(item)
yield item
first = buffer.popleft()
if first[-1] in cls.TERMINATORS:
start_words[tuple(buffer)] += 1