【问题标题】:Python 3.x Shifting RangesPython 3.x 移位范围
【发布时间】:2014-12-25 08:19:29
【问题描述】:

假设我有这样的范围:

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)

【问题讨论】:

    标签: python loops python-3.x range


    【解决方案1】:

    您可以将范围包装在生成器表达式中,即时应用移位和取​​模:

    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]
    

    【讨论】:

    • 感谢您的详细回答。 (我猜至少允许一条包含感谢的评论而不会被视为垃圾邮件):P
    【解决方案2】:

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

    from itertools import chain
    
    shifted = chain(range(7, 10), range(7))
    for x in shifted:
        print(x)
    

    【讨论】:

      【解决方案3】:

      您可以使用 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]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-10-23
        • 2022-10-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-31
        • 1970-01-01
        • 2017-06-13
        相关资源
        最近更新 更多