Python懒惰列表

时间:2008-10-27 19:32:53

标签: python

我想创建自己的集合,它具有python列表的所有属性,并且还知道如何在数据库中保存/加载自身。此外,我想使加载隐式和惰性,因为它不会在创建列表时发生,而是等到它第一次使用时。

我是否可以覆盖一个__xxx__方法,以便在首次使用任何列表属性时加载列表(例如lengetitemiter ...等)而不必全部覆盖它们?

5 个答案:

答案 0 :(得分:7)

不完全是。对于模拟其他而不是列表的东西,有__getattribute__,但遗憾的是,Python不认为x[y]x(y)等运算符正好x.__getitem__(y)x.__call__(y)相同。像这样的运算符是类的属性,而不是实例的属性,如下所示:

>>> class x(object):
...     def __getattribute__(self, o):
...         print o
... 
>>> x()[3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'x' object does not support indexing

但是,您可以利用Python的动态特性来有效地消除这种区别。如果你主要关注的是保存自己的输入,并减少需要维护的代码,你可以这样做:

class override(object):
    def __init__(self, methodName):
        self.methodName = methodName

    def __get__(self, oself, cls):
        oself._load(self.methodName)
        return getattr(super(oself.__class__, oself), self.methodName)

class LazyList(list):
    def _load(self, name):
        print 'Loading data for %s...' % (name,)

    for methodName in set(dir(list)) - set(dir(object)):
        locals()[methodName] = override(methodName)

你可能不想在现实生活中使用dir(),但是一个合适的固定字符串列表可以作为替代。

答案 1 :(得分:7)

不是,但5就足够了:

from collections import MutableSequence

class Monitored(MutableSequence):
    def __init__(self):
        super(Monitored, self).__init__()
        self._list = []

    def __len__(self):
        r = len(self._list)
        print "len: {0:d}".format(r)
        return r

    def __getitem__(self, index):
        r = self._list[index]
        print "getitem: {0!s}".format(index)
        return r

    def __setitem__(self, index, value):
        print "setitem {0!s}: {1:s}".format(index, repr(value))
        self._list[index] = value

    def __delitem__(self, index):
        print "delitem: {0!s}".format(index)
        del self._list[index]

    def insert(self, index, value):
        print "insert at {0:d}: {1:s}".format(index, repr(value))
        self._list.insert(index, value)

检查某些内容是否实现整个列表界面的正确方法是检查它是否是MutableSequence的子类。在collections模块中找到的MutableSequence是一个的ABC,有两个原因:

  1. 允许您创建自己的类来模拟内部容器类型,以便它们可以在普通内置函数的任何位置使用。

  2. 用作isinstanceissubclass的参数来验证对象是否实现了必要的功能:

  3. >>> isinstance([], MutableSequence)
    True
    >>> issubclass(list, MutableSequence)
    True
    

    我们的Monitored课程的工作原理如下:

    >>> m = Monitored()
    >>> m.append(3)
    len: 0
    insert at 0: 3
    >>> m.extend((1, 4))
    len: 1
    insert at 1: 1
    len: 2
    insert at 2: 4
    >>> m.l
    [3, 1, 4]
    >>> m.remove(4)
    getitem: 0
    getitem: 1
    getitem: 2
    delitem: 2
    >>> m.pop(0)   # after this, m.l == [1]
    getitem: 0
    delitem: 0
    3
    >>> m.insert(0, 4)
    insert at 0: 4
    >>> m.reverse()   # After reversing, m.l == [1, 4]
    len: 2
    getitem: 1
    getitem: 0
    setitem 0: 1
    setitem 1: 4
    >>> m.index(4)
    getitem: 0
    getitem: 1
    1
    

答案 2 :(得分:3)

答案 3 :(得分:1)

没有一种方法。你必须重新定义它们中的很多。 MutableSequence似乎是现代的做法。这是一个适用于Python 2.4 + ::

的版本
class LazyList(list):
    """List populated on first use."""
    def __new__(cls, fill_iter):

        class LazyList(list):
            _fill_iter = None

        _props = (
            '__str__', '__repr__', '__unicode__',
            '__hash__', '__sizeof__', '__cmp__', '__nonzero__',
            '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__',
            'append', 'count', 'index', 'extend', 'insert', 'pop', 'remove',
            'reverse', 'sort', '__add__', '__radd__', '__iadd__', '__mul__',
            '__rmul__', '__imul__', '__contains__', '__len__', '__nonzero__',
            '__getitem__', '__setitem__', '__delitem__', '__iter__',
            '__reversed__', '__getslice__', '__setslice__', '__delslice__')

        def lazy(name):
            def _lazy(self, *args, **kw):
                if self._fill_iter is not None:
                    _fill_lock.acquire()
                    try:
                        if self._fill_iter is not None:
                            list.extend(self, self._fill_iter)
                            self._fill_iter = None
                    finally:
                        _fill_lock.release()
                real = getattr(list, name)
                setattr(self.__class__, name, real)
                return real(self, *args, **kw)
            return _lazy

        for name in _props:
            setattr(LazyList, name, lazy(name))

        new_list = LazyList()
        new_list._fill_iter = fill_iter
        return new_list

答案 4 :(得分:0)

不,没有。