Я хотел бы создать свою собственную коллекцию, которая имеет все атрибуты списка Python, а также знает, как сохранять / загружать себя в / из базы данных. Также я хочу сделать загрузку неявной и ленивой, так как она не происходит в момент создания списка, а ждет, пока не будет впервые использована.
Есть ли один метод __xxx__, который я могу переопределить для загрузки списка при первом использовании любого свойства списка (например, len, getitem, iter ... и т. д.) Без необходимости переопределения их всех?






Нет, нет.
Не совсем. Для эмуляции Другие, а не списков, есть __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() в реальной жизни, но подходящий фиксированный список строк может сработать в качестве замены.
В функции _load вы должны вызвать list. <function> (self, ..), чтобы добавить данные для предотвращения рекурсивной загрузки. Пример: list.append (self, value)
нужно ли было ссылаться на сайт для вставки?
Кроме того, я не совсем понимаю, к чему вы идете, с первой частью вашего ответа: вы хотели определить __getitem __ ()?
Спасибо за редактирование, хоп, я не знаю, зачем мне делать ссылку на сайт для вставки. Я явно не собирался определять __getitem__; весь смысл этой части ответа состоял в том, чтобы проиллюстрировать, что не существует метода двойного подчеркивания не замужем, который можно было бы переопределить, вам придется переопределить многие из них.
Не не замужем, но 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. ABC, найденные в модуле collections, из которых 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
@Andrew Исправлено изменением строки форматирования для index с "{0:d}" на "{0!s}" в трех методах, где index может быть объектом среза.
Я написал вопрос о каком-то странном поведении, которое я обнаружил, не могли бы вы взглянуть?
Нет единого метода. Придется переопределить довольно много из них. 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
Ищете скрытый метод dwiw, а? «Делай, что хочу».