Python 延迟列表
我想创建一个自己的集合,这个集合不仅具备Python列表的所有特点,还能知道怎么把自己保存到数据库里或者从数据库里加载出来。此外,我希望加载的过程是隐式和懒加载的,也就是说,它不是在创建列表的时候就发生,而是等到第一次使用的时候再加载。
有没有一个单独的__xxx__
方法可以重写,这样在第一次使用任何列表属性(比如len
、getitem
、iter
等等)的时候就能加载列表,而不需要一个个重写这些方法呢?
5 个回答
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()
,但可以用一个合适的固定字符串列表来替代。
8
不是一个就够,而是五个就足够了:
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
模块中的抽象基类(ABCs),其中 MutableSequence
就是一个,存在的原因有两个:
让你可以创建自己的类,模仿内部容器类型,这样它们就可以在任何需要普通内置类型的地方使用。
可以作为
isinstance
和issubclass
的参数,用来验证一个对象是否实现了必要的功能:
>>> 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
不,没有。