如何修改Python运算符?

时间:2014-01-23 20:30:35

标签: python iterator

这是我对Fib squence的迭代器:

class fibo:

  def __init__(self, n=0):
     self.n = n
     self.x1 = 0
     self.x2 = 1

  def next(self):
      if self.n == 0:
          raise StopIteration
      else:
          self.n = self.n -1
          tmp = self.x1
          self.x1, self.x2 = self.x2, self.x1 + self.x2
          return tmp

  def __iter__(self):
      return self

结果是

>>> [i for i in fibo(15)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]

如何修改代码,使得迭代调用不是迭代前n个斐波那契数,而是迭代接下来的n个数,例如,????

>>> f=fibo(5)

>>> [i for i in f]
[0, 1, 1, 2, 3]

>>> [i for i in f]
[5, 8, 13, 21, 34]
>>> [i for i in f]
[55, 89, 144, 233, 377]
>>> [i for i in f]
[610, 987, 1597, 2584, 4181]

4 个答案:

答案 0 :(得分:2)

要获得所需的确切语法要么完全是非法的,要么在Python中非常不鼓励,因为迭代器协议要求迭代器在完成一次之后继续产生StopIteration。来自PEP 234,它引入了迭代器:

  - Once a particular iterator object has raised StopIteration, will
  it also raise StopIteration on all subsequent next() calls?
  Some say that it would be useful to require this, others say
  that it is useful to leave this open to individual iterators.
  Note that this may require an additional state bit for some
  iterator implementations (e.g. function-wrapping iterators).

  Resolution: once StopIteration is raised, calling it.next()
  continues to raise StopIteration.

编辑再想一想,我认为你想要的实际上是“合法的”Python,因为列表理解[i for i in fibo]隐含地调用了fibo上的__iter__方法,因此或多或少要求一个新的迭代器(即使这是由同一个对象实现的)。因此,实现所需行为的正确方法是:

class Fibo:
    def __init__(self, n):
        self.n = n
        self.cnt = n
        self.x1 = 0
        self.x2 = 1

    def next(self):
        if self.cnt > 0:
            self.cnt -= 1
            tmp = self.x1
            self.x1, self.x2 = self.x2, self.x1 + self.x2
            return tmp
        else: 
            raise StopIteration # keeps raising

    def __iter__(self):
        self.cnt = self.n # reset the counter here
        return self

其中的工作原理如下:

In [32]: f = Fibo(3)
In [33]: it = iter(f)
In [34]: it.next()
Out[34]: 0
In [35]: it.next()
Out[35]: 1
In [36]: it.next()
Out[36]: 1
In [37]: it.next()
-> StopIteration
In [38]: it.next() # repeated call keeps raising
-> StopIteration
In [39]: it = iter(f) # explicitly reset iterator
In [40]: it.next()
Out[40]: 2
In [41]: it.next()
Out[41]: 3
In [42]: it.next()
Out[42]: 5
In [43]: it.next()
-> StopIteration

这显示了所需的行为:它在耗尽时不断提升StopIteration,您需要明确调用iter来重置它。这与C.B.的版本略有不同,后者简单地包装,无需重置:

In [45]: f = fibo(3)
In [46]: it = iter(f)
In [47]: it.next()
Out[47]: 0
In [48]: it.next()
Out[48]: 1
In [49]: it.next()
Out[49]: 1
In [50]: it.next()
-> StopIteration
In [51]: it.next() # no need to reset!
Out[51]: 2
In [52]: it.next()
Out[52]: 3
In [53]: it.next()
Out[53]: 5
In [54]: it.next()
-> StopIteration

答案 1 :(得分:2)

这适用于2.7

class fibo:

    def __init__(self, n=0):
        self.n = n
        self.x1 = 0
        self.x2 = 1
        self.current = 0 #We won't decrease n, but instead increase current until it
                             #Equals n

    def next(self):
        if self.n == self.current:
            self.current = 0
            raise StopIteration
        else:
            self.current +=1
            tmp = self.x1
            self.x1, self.x2 = self.x2, self.x1 + self.x2
            return tmp

    def __iter__(self):
        return self

f = fibo(5)
print [i for i in f]
print [i for i in f]

输出

[0, 1, 1, 2, 3]
[5, 8, 13, 21, 34]

答案 2 :(得分:1)

要获得您想要的内容,但语法不同,您可以使用What is the most “pythonic” way to iterate over a list in chunks?的答案。特别是,此one of the answers的代码段:

def grouper(iterable, n, fillvalue=None):
    args = [iter(iterable)] * n
    return izip_longest(*args, fillvalue=fillvalue)

此函数会将可迭代切片为相同大小的“块”。您可以将其与现有fibo类的非终止版本一起使用:

class fibo:
  def __init__(self):
      self.x1 = 0
      self.x2 = 1

  def next(self):
      tmp = self.x1
      self.x1, self.x2 = self.x2, self.x1 + self.x2
      return tmp

  def __iter__(self):
      return self

合并后,您现在可以执行以下操作:

>>> f = grouper(fibo(), 5)
>>> f.next()
(0, 1, 1, 2, 3)
>>> f.next()
(5, 8, 13, 21, 34)
>>> f.next()
(55, 89, 144, 233, 377)

请注意,此修改后的fibo永远不会停止迭代,因此您需要小心将其提供给任何尝试热切迭代到最后的内容。

顺便说一句,这是使用生成器而不是类的迭代器的替代实现:

def fibg():
    x1 = 0
    x2 = 1
    while True:
        yield x1
        x1, x2 = x2, x1 + x2

答案 3 :(得分:0)

我认为你所追求的行为的最佳匹配是一个迭代器,它接受额外的项目数来产生:

class fibo:
    def __init__(self, n=0):
        self.x1 = 0
        self.x2 = 1
        self.n = n
    def __iter__(self):
        return self
    def next(self, more=0):
        if more:
            self.n += more
            return
        while self.n:
           self.n -= 1
           current, self.x1, self.x2 = self.x1, self.x2, self.x2 + self.x1
           return current
        raise StopIteration

虽然这个迭代器是technically broken,但它避免了最严重的迭代器问题 - 在没有用户干预的情况下出现新项目;换句话说:一旦StopIteration被提出,它将继续提高,直到你明确告诉它你想要更多项目:

>>> f = fibo(4)
>>> for n in f:
...   print n
... 
0
1
1
2
>>> f.next(7)
>>> for n in f:
...   print n
... 
3
5
8
13
21
34
55