首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python环绕列表

Python环绕列表
EN

Code Review用户
提问于 2017-01-29 18:04:41
回答 2查看 9.1K关注 0票数 19

我希望获得创建类似Python内置类型的数据结构的经验。作为第一个练习,我编写了一个WraparoundList类,它意味着与内置的list完全相同,除了访问超出范围的元素“包装”之外。

目标:

  • 唯一与list不同的行为是当使用[]显式索引时。
  • 应该看上去和感觉上都像Python内置的,也就是说,在collections模块中看起来不会太不合适。
  • 应该与Python2.7.x和3.x兼容(尽管我只在2.7.13上进行了测试)。

下面是包含doctest的完整源代码:

代码语言:javascript
复制
#!/usr/bin/env python

from sys import maxint as MAXINT

class WraparoundList(list):
    """A list whose index wraps around when out of bounds.

    A `WraparoundList` is the same as an ordinary `list`, 
    except that out-of-bounds indexing causes the index 
    value to wrap around. The wrapping behavior is as if
    after reaching the last element, one returned to the 
    other end of the list and continued counting.

    >>> x = WraparoundList('abcd')
    >>> x
    ['a', 'b', 'c', 'd']
    >>> x[3]
    'd'
    >>> x[4] # wraps to x[0]
    'a'
    >>> x[-6] = 'Q' # wraps to x[-2]
    >>> x
    ['a', 'b', 'Q', 'd']
    >>> del x[7] # wraps to x[3]
    >>> x 
    ['a', 'b', 'Q']

    Indices used in out-of-range slices also wrap around.
    If the slice's `start` or `stop` is out-of-bounds, it 
    gets wrapped around.

    >>> x = WraparoundList('abcd')
    >>> x
    ['a', 'b', 'c', 'd']
    >>> x[:10] # wraps to x[:2]
    ['a', 'b']
    >>> x[-7:3] # wraps to x[-3:3]
    ['b', 'c']

    The one way in which slicing a `WraparoundList` differs 
    from slicing an ordinary `list` is the case of using the
    list length as the upper limit.

    >>> x = WraparoundList('abcd')
    >>> x
    ['a', 'b', 'c', 'd']
    >>> x[2:]
    ['c', 'd']
    >>> x[2:4] # wraps to x[2:0]
    []

    Initializing a `WraparoundList` with a nested iterable
    does not cause inner indices to wrap. To have a multi-
    dimensional `WraparoundList`, all the elements of the 
    outer `WraparoundList` must also be `WraparoundList`s.

    >>> x = WraparoundList([list('abc'), list('def')])
    >>> x
    [['a', 'b', 'c'], ['d', 'e', 'f']]
    >>> x[3]
    ['d', 'e', 'f']
    >>> x[3][5]
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    IndexError: list index out of range
    >>> y = WraparoundList([WraparoundList(i) for i in x])   
    >>> y[3][5]
    'f'
    """
    def __getitem__(self, i):
        """x.__getitem__(i) <=> x[i]"""
        if isinstance(i, slice):
            return list.__getitem__(self, self._wrap_slice(i))
        else:
            return list.__getitem__(self, self._wrap_index(i))

    def __getslice__(self, i, j):
        """x.__getslice__(i, j) <=> x[i:j]"""
        return self.__getitem__(slice(i, j, None))

    def __setitem__(self, i, y):
        """x.__setitem__(i, y) <=> x[i] = y"""
        if isinstance(i, slice):
            list.__setitem__(self, self._wrap_slice(i), y)
        else:
            list.__setitem__(self, self._wrap_index(i), y)

    def __setslice__(self, i, j):
        """x.__setslice__(i, j) <=> x[i:j] = y"""
        self.__setitem__(slice(i, j, None))

    def __delitem__(self, i):
        """x.__delitem__(i, y) <=> del x[i]"""
        if isinstance(i, slice):
            list.__delitem__(self, self._wrap_slice(i))
        else:
            list.__delitem__(self, self._wrap_index(i))

    def __delslice__(self, i, j):
        """x.__delslice__(i, j) <=> del x[i:j]"""
        self.__delitem__(slice(i, j, None))

    def _wrap_index(self, i):
        _len = len(self)
        if i >= _len:
            return i % _len
        elif i < -_len:
            return i % (-_len)
        else:
            return i

    def _wrap_slice(self, slc):
        if slc.start is None:
            start = None
        else:
            start = self._wrap_index(slc.start) 
        if slc.stop is None:
            stop = None
        elif slc.stop == MAXINT:
            # __*slice__ methods treat absent upper bounds as sys.maxint, which would
            # wrap around to a system-dependent (and probably unexpected) value. Setting 
            # to `None` in this case forces the slice to run to the end of the list.
            stop = None
        else:
            stop = self._wrap_index(slc.stop)
        step = slc.step
        return slice(start, stop, step)

def main():
    pass

if __name__ == '__main__':
    main()
EN

回答 2

Code Review用户

回答已采纳

发布于 2017-01-29 18:48:59

  1. 这是很好的文档和注释的代码。
  2. docstring说:WraparoundList切片不同于普通list切片的一种方法是使用列表长度作为上限。但这并不是全部的事情--普通的list也可以使用大于列表长度的值进行切片,在这种情况下,WraparoundList也有不同的行为:>>> x= 1,2,3 >>> x*10 >>>x= WraparoundList(x) >>> x*10
  3. 代码不能移植到Python3,因为没有sys.maxint (Python3中的所有整数都是“长”的)。我建议这样做:在Python2.7中尝试:#,当调用__*slice__方法时没有“停止”#值,则传递sys.maxint。从sys导入maxint作为NO_STOP,除了ImportError:#Python3没有sys.maxint或使用__*slice__方法。NO_STOP = object()我更喜欢像NO_STOP这样的名称,因为它传递的是意图,而不是实现。
  4. 如果列表为空,_wrap_index将引发ZeroDivisionError:>>> w= WraparoundList([]) >>> w0回溯(最近一次调用):文件"",第1行,在文件“cr153920.py”中,第79行,在__getitem__返回list.__getitem__(self,self._wrap_index(i))文件"cr153920.py",第110行,在_wrap_index返回I% _len ZeroDivisionError:整数除法或以零引发异常是正确的。但我希望能得到一个IndexError
  5. 代码直接调用list.__getitem__,而不是通过super函数调用。但是,如果某个人的另一个类C也继承了list并重写了__getitem__方法,并通过继承组合了WraparoundListC,这就有一个令人不满意的结果:类D(WraparoundList,C):pass然后D()[0]调用WraparoundList.__getitem__,后者调用list.__getitem__,但是C.__getitem__从未被调用,这与人们所期望的相反。如果您想支持WraparoundList的子类,那么您需要编写:返回超级(WraparoundList,self).__getitem__(self._wrap_slice(i))等等。
  6. 只要稍微重构一下,就可以避免一些重复。特别是,如果您有这样的方法: def _wrap_arg(self,i):如果isinstance(i,片):返回self._wrap_slice(i) be :返回self._wrap_index(i),那么您就可以编写: def __getitem__(self,i):“x.__getitem__(I) <=> x我”“ReverSuper(WraparoundList,.__getitem__(self._wrap_arg(I))等。
  7. 一旦您完成了上面的重构,您将看到_wrap_slice只从一个地方调用,因此它可以在其使用点被内联。
  8. 不需要包含一个空的main函数或一个if __name__ == '__main__':部分--如果没有什么可做的,那么就没有必要编写代码来完成它。
票数 14
EN

Code Review用户

发布于 2017-12-15 00:03:15

除了Gareth的出色回答:由于WraparoundList和通常的list的行为不同(例如,当使用大于列表长度的值进行切片时),您的类不尊重Liskov代换原理 (参见较不正式的解释)。

简单地说,因为一些使用list的代码在使用WraparoundList时会表现不同,所以即使尊重"WraparoundList是一个列表“关系,WraparoundList也不应该从list继承。

改变这种情况的一种方法可能是停止从list继承,而是在内部使用list存储数据(组合重于继承)。

票数 0
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/153920

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档