2014-10-29 51 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]