【问题标题】:python - Iterator which duplicates elements of iterable objectpython - 复制可迭代对象元素的迭代器
【发布时间】:2018-08-19 14:49:16
【问题描述】:

我需要编写一个函数duplicate(it),它返回迭代器复制可迭代it 的元素。示例:如果 it = [1, 2, 3]list(duplicate(it)) == [1, 1, 2, 2, 3, 3]。我尝试为列表实现DoubleIterator 类和duplicate(it) 函数(在这种情况下不确定DoubleIterator 的正确实现方式,所以欢迎提供有用的建议):

class DoubleIterator():
    def __init__(self, data):
        self.data = data
        self.pos = 0
        self.limit = len(data)
        self.dup = 0

    def next(self): # __next__ for Python 3
        if self.dup != 0 and self.dup % 2 == 0:
            self.pos = self.pos + 1
        if self.pos < self.limit:
            self.dup += 1
            return self.data[self.pos]
        else:
            raise StopIteration

    def __iter__(self):
        return self

def duplicate(it):
    itr = DoubleIterator(it)
    return iter(itr)

它适用于 list(duplicate(it)) == [1, 1, 2, 2, 3, 3] 示例和类似的东西:

dup = duplicate([1, 2, 3])
print next(dup) #1
print next(dup) #1
print next(dup) #2
print next(dup) #2
print next(dup) #3
print next(dup) #3

但是这个函数的任务测试之一是dup = duplicate(iter([1, 2, 3])),它给出了一个错误:TypeError: object of type 'listiterator' has no len()。我应该如何正确实现duplicate函数和迭代器类?

【问题讨论】:

  • 你为什么还要关心这里的limit 或按索引访问元素?对于self.data 中的每个元素,只需多次生成它 - 你不需要知道/关心它的长度,或者即使它有一个......

标签: python iterator iterable


【解决方案1】:

我只是作为生成器来做

def duplicate(it,n=2):
    for x in it:
        for _ in range(n):
            yield x

【讨论】:

    【解决方案2】:

    你有点把事情复杂化了。您需要做的就是获取下一个元素并生成两次。您确实需要将输入视为迭代器,而不是序列。只有像列表这样的序列有长度并且可以被索引,迭代器所能做的就是给你下一个值直到它用完。

    对于基于类的迭代器,这意味着您需要存储要重复的元素,以便对next() 的所有其他调用都可以生成该存储值,而其他调用则从输入迭代器中获取下一个元素:

    class DoubleIterator:
        _sentinel = object()
    
        def __init__(self, iterator):
            self._data = iter(iterator)
            self._repeat = self._sentinel
    
        def next(self): # __next__ for Python 3
            if self._repeat is self._sentinel:
                # get next element to yield, can raise StopIteration
                self._repeat = next(self._data)
                return self._repeat
            # repeat last element
            element = self._repeat
            self._repeat = self._sentinel
            return element
    
        def __iter__(self):
            return self
    

    我使用了一个唯一的标记来确保支持输入迭代器中的 None 值,并且我在输入迭代器上调用了 iter() 以确保我们始终可以在该对象上调用 next()

    但是,要容易得多,只需在这里编写一个生成器函数,然后将每个元素生成两次:

    def double_iterator(iterator):
        for elem in iterator:
            yield elem
            yield elem
    

    您可以通过循环进一步概括这一点,但如果您只需要将元素加倍,我会坚持使用更简单的 double yield

    【讨论】:

    • 甚至:yield from (el for el in iterator for _ in range(2))... 如果需要,您可以概括该功能...
    • @JonClements: yield from 在你可以使用 return (el for el in iterator for _ in range(2)) 的时候太过分了。
    • 确实...实例都将是 generator 实例,但我保留了 yield from 以避免引入看起来与 OP 一直在使用的完全不同的东西...
    • 您可以使用... for _ in (1,2)使其更短(更快)
    • @VPfB:我认为使用range() 的目的是为了无需对所需的重复进行硬编码。
    猜你喜欢
    • 1970-01-01
    • 2015-01-01
    • 1970-01-01
    • 2014-04-26
    • 2015-01-19
    • 2019-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多