Python ленивый список - PullRequest
       16

Python ленивый список

14 голосов
/ 27 октября 2008

Я хотел бы создать свою собственную коллекцию, которая имеет все атрибуты списка Python, а также знает, как сохранить / загрузить себя в / из базы данных. Также я хочу сделать загрузку неявной и ленивой, так как она не происходит в момент создания списка, а ждет, пока он не будет использован впервые.

Существует ли один __xxx__ метод, который я могу переопределить, чтобы загрузить список при первом использовании любого свойства списка (например, len, getitem, iter ... и т. Д.) Без необходимости переопределять их все

Ответы [ 5 ]

7 голосов
/ 24 февраля 2011

Не один , но достаточно 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 - одна, существует по двум причинам:

  1. , чтобы позволить вам создавать свои собственные классы, эмулирующие внутренние типы контейнеров, чтобы их можно было использовать везде, где есть обычные встроенные функции.

  2. для использования в качестве аргумента для 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
7 голосов
/ 28 октября 2008

Не совсем. Для эмуляции вещей , отличных от списков, есть __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() в реальной жизни, но подходящий фиксированный список строк может заменить его.

3 голосов
/ 27 октября 2008
1 голос
/ 05 сентября 2013

Нет ни одного метода. Вы должны переопределить их довольно много. 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
0 голосов
/ 28 октября 2008

Нет, нет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...