我想创建自己的集合,它具有python列表的所有属性,并且还知道如何在数据库中保存/加载自身。此外,我想使加载隐式和惰性,因为它不会在创建列表时发生,而是等到它第一次使用时。
我是否可以覆盖一个__xxx__
方法,以便在首次使用任何列表属性时加载列表(例如len
,getitem
,iter
...等)而不必全部覆盖它们?
答案 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,有两个原因:
允许您创建自己的类来模拟内部容器类型,以便它们可以在普通内置函数的任何位置使用。
用作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 :(得分: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)
不,没有。