Noc*_*wer 1 python iterator dry generator python-3.x
下面的方法在我的课堂上,尝试在完成工作之前准备好自身。底漆懒于完成其后续的处理循环。在这两个循环中重复了五行,对于我来说,消除重复的最佳方法可能并不明显。
@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
Run Code Online (Sandbox Code Playgroud)
是否有一种有效且清晰的方法可以在类或方法中一次编写最后五行?
附录
在回答这个问题就什么prefix以及suffix是,这里是Deque类:
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
Run Code Online (Sandbox Code Playgroud)
第二版
考虑到其他人的意见后,编写了以下方法以提高效率:
@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
Run Code Online (Sandbox Code Playgroud)
第三版
该方法的第三个版本已改编自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')
Run Code Online (Sandbox Code Playgroud)
最终版本
此方法比我的第一个版本好得多-感谢在这里为我提供帮助的人们。
@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')
Run Code Online (Sandbox Code Playgroud)
使用for-loop:
@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')
Run Code Online (Sandbox Code Playgroud)
关于第二个版本的一些想法:count使用时不需要islice:
for item in islice(iterator, n):
buffer.append(item)
yield item
if len(buffer) < n:
raise ValueError('iterable was too short to satisfy n')
Run Code Online (Sandbox Code Playgroud)
进一步的重构导致:
@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
Run Code Online (Sandbox Code Playgroud)