Универсальный ленивый атрибут getter / setter - PullRequest
0 голосов
/ 25 марта 2019

Этот вопрос основан на этом вопросе относительно ленивых атрибутов для классов Python.

Мне действительно нравится решение, данное там:

Вот пример реализации отложенного декоратора свойств:

import functools

def lazyprop(fn):
    attr_name = '_lazy_' + fn.__name__

    @property
    @functools.wraps(fn)
    def _lazyprop(self):
        if not hasattr(self, attr_name):
            setattr(self, attr_name, fn(self))
        return getattr(self, attr_name)

    return _lazyprop


class Test(object):

    @lazyprop
    def a(self):
        print 'generating "a"'
        return range(5)

Интерактивная сессия:

>>> t = Test()
>>> t.__dict__
{}
>>> t.a
generating "a"
[0, 1, 2, 3, 4]
>>> t.__dict__
{'_lazy_a': [0, 1, 2, 3, 4]}
>>> t.a
[0, 1, 2, 3, 4]

Это решение позволяет вам создать @lazyprop для любого атрибута. Тем не менее, вы должны написать метод для каждого атрибута, который вы хотите быть ленивым. Мне нужно что-то, что будет работать для атрибутов, имена которых я не буду знать заранее (которых может быть много).

Эти атрибуты являются фреймами данных, считанными из файлов hdf5. Каждый файл содержит много разных таблиц, названия которых я не знаю. У меня есть отличная функция, get_all_table_names(filename), которая возвращает имена всех таблиц в файле. В настоящее время я перебираю все имена и читаю их одно за другим. Однако существует несколько десятков ГБ данных, для считывания которых требуется несколько минут.

Есть ли способ на самом деле читать в таблице, только когда метод вызывает эту таблицу? Приведенный здесь пример идеален, за исключением того, что мне нужно знать имя таблицы заранее.

EDIT

Код для загрузки данных из файла HDF5 в Pandas DataFrame выглядит следующим образом.

df = read_to_pandas(directory_of_files, 'table_name', number_of_files_to_read)

1 Ответ

0 голосов
/ 25 марта 2019

Вот общий шаблон, показывающий, как генерировать класс на лету с динамическими ленивыми атрибутами:

import functools
import types


def lazyprop(added_value):
    """ Slightly generalize lazy attribute property decorator.
        (i.e. a decorator-factory)
    """
    def prop(fn):
        attr_name = '_lazy_' + fn.__name__ + str(added_value)

        @property
        @functools.wraps(fn)
        def _lazyprop(self):
            if not hasattr(self, attr_name):
                setattr(self, attr_name, fn(self, added_value))
            return getattr(self, attr_name)

        return _lazyprop

    return prop


def make_class(class_name, attrs):

    # Generic methods and class __dict__.
    def __init__(self):
        print('creating instance of class', self.__class__.__name__)

    def getter(self, added_value):
        return 41 + added_value

    cls_dict = {
        '__init__': __init__,
        '__repr__': lambda self: 'class name: %s' % class_name,
    }

    # Create and added lazy attributes.
    for i, attr_name in enumerate(attrs):
        cls_dict[attr_name] = lazyprop(i)(getter)

    cls = types.new_class(class_name, (), {}, lambda ns: ns.update(cls_dict))
    cls.__module__ = __name__

    return cls


if __name__ == '__main__':

    Foobar = make_class('Foobar', ('attr1', 'attr2'))

    foobar = Foobar()    # -> creating instance of class Foobar
    print(foobar)        # -> class name: Foobar
    print(foobar.attr1)  # -> 41
    print(foobar.attr2)  # -> 42
...