2014-10-29 45 views
3

假设我有一个范围是这样的:的Python 3.x的换挡范围

x = range(10) 

这将具有以下值作为一个列表:

list(x)  # Prints [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

我想转移该范围(可能多次) 并迭代结果,例如

   #  [7, 8, 9, 0, 1, 2, 3, 4, 5, 6] 

创建等效列表不是问题。但我想 知道是否有可能创造这样的事情作为一个 范围,以节省内存,当然还有一定的空间,这将 是很好,如果解决方案可以为大约为高性能为:

for i in range(1000000) 

回答

6

可以包裹在发电机表达的范围内,将所述移位和模上飞:

def shifted_range(rangeob, shift): 
    size, shift = rangeob.stop, shift * rangeob.step 
    return ((i + shift) % size for i in rangeob) 

演示:

>>> def shifted_range(rangeob, shift): 
...  size, shift = rangeob.stop, shift * rangeobj.step 
...  return ((i + shift) % size for i in rangeob) 
... 
>>> range_10 = range(10) 
>>> list(shifted_range(range_10, 3)) 
[3, 4, 5, 6, 7, 8, 9, 0, 1, 2] 
>>> list(shifted_range(range_10, 7)) 
[7, 8, 9, 0, 1, 2, 3, 4, 5, 6] 
>>> range_10_2 = range(0, 10, 2) 
>>> list(shifted_range(range_10_2, 4)) 
[8, 0, 2, 4, 6] 

你能做出这样一个包装对象,以及:

class RangeShift: 
    def __init__(self, rangeob, shift): 
     self._range = rangeob 
     self.shift = shift 

    @property 
    def start(self): 
     r = self._range 
     return (r.start + self.shift * r.step) % r.stop 

    @property 
    def stop(self): 
     r = self._range 
     return (r.stop + self.shift * r.step) % r.stop 

    def index(self, value): 
     idx = self._range.index(value) 
     return (idx - self.shift) % len(self._range) 

    def __getattr__(self, attr): 
     return getattr(self._range, attr) 

    def __getitem__(self, index): 
     r = self._range 
     return (r[index] + self.shift * r.step) % r.stop 

    def __len__(self): 
     return len(self._range) 

    def __iter__(self): 
     size, shift = self._range.stop, self.shift * self._range.step 
     return ((i + shift) % size for i in self._range) 

这将表现就像原来的范围,但应用转移到生产的所有值。它甚至可以让你改变班次!

演示:

>>> range_10 = range(10) 
>>> shifted = RangeShift(range_10, 7) 
>>> len(shifted) 
10 
>>> shifted.start 
7 
>>> shifted.stop 
7 
>>> shifted.step 
1 
>>> shifted[3] 
0 
>>> shifted[8] 
5 
>>> list(shifted) 
[7, 8, 9, 0, 1, 2, 3, 4, 5, 6] 
>>> shifted.shift = 3 
>>> list(shifted) 
[3, 4, 5, 6, 7, 8, 9, 0, 1, 2] 
>>> range_10_2 = range(0, 10, 2) 
>>> shifted_10_2 = RangeShift(range_10_2, 4) 
>>> list(shifted_10_2) 
[8, 0, 2, 4, 6] 
此包装现在支持

绝招:倒车变速范围:

>>> list(reversed(shifted)) 
[2, 1, 0, 9, 8, 7, 6, 5, 4, 3] 
>>> list(reversed(shifted_10_2)) 
[6, 4, 2, 0, 8] 
+0

感谢这个详细的解答。 (我想至少有一封包含感谢的评论是允许的,而不会被视为垃圾邮件):P – Nimi 2014-10-29 15:27:34

1

我想最简单的方法就是chain两个范围:

from itertools import chain 

shifted = chain(range(7, 10), range(7)) 
for x in shifted: 
    print(x) 
1

你可以使用itertools来链接两个范围。此代码的工作,即使范围具有一步> 1.

import itertools 

def shift_range(r, s): 
    return itertools.chain(range(r.start + s*r.step, r.stop, r.step), 
          range(r.start, r.start + s*r.step, r.step)) 

测试:

>>> list(shift_range(range(10), 5)) 
[5, 6, 7, 8, 9, 0, 1, 2, 3, 4] 
>>> list(shift_range(range(3, 30, 3), 5)) 
[18, 21, 24, 27, 3, 6, 9, 12, 15]