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

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

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

Ищете скрытый метод dwiw, а? «Делай, что хочу».

S.Lott 27.10.2008 22:38
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
14
1
3 505
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

__getattribute__

И это все, что я могу сказать об этом ... :)

Kevin Little 28.10.2008 00:33

@ Глиф, который вы вставили, будет работать, если вы определили getitem

Dan 28.10.2008 06:41
Ответ принят как подходящий

Нет, нет.

Не совсем. Для эмуляции Другие, а не списков, есть __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)

Staale 26.01.2009 16:45

нужно ли было ссылаться на сайт для вставки?

user3850 29.03.2011 22:16

Кроме того, я не совсем понимаю, к чему вы идете, с первой частью вашего ответа: вы хотели определить __getitem __ ()?

user3850 29.03.2011 22:17

Спасибо за редактирование, хоп, я не знаю, зачем мне делать ссылку на сайт для вставки. Я явно не собирался определять __getitem__; весь смысл этой части ответа состоял в том, чтобы проиллюстрировать, что не существует метода двойного подчеркивания не замужем, который можно было бы переопределить, вам придется переопределить многие из них.

Glyph 19.04.2011 21:33

В документации версии 2.x для locals() говорится, что содержимое возвращаемого словаря не должно изменяться - так что использование этой техники - хорошая идея?

martineau 15.03.2013 22:55

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

  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

@Andrew Исправлено изменением строки форматирования для index с "{0:d}" на "{0!s}" в трех методах, где index может быть объектом среза.

Lauritz V. Thaulow 31.05.2012 22:11

Я написал вопрос о каком-то странном поведении, которое я обнаружил, не могли бы вы взглянуть?

Andrew Roberts 31.05.2012 23:49

Нет единого метода. Придется переопределить довольно много из них. 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

Другие вопросы по теме