Nano Hash - криптовалюты, майнинг, программирование

Настроить словарь лениво

Допустим, у меня есть этот словарь на питоне, определенный на уровне модуля (mysettings.py):

settings = {
    'expensive1' : expensive_to_compute(1),
    'expensive2' : expensive_to_compute(2),
    ...
}

Я хотел бы, чтобы эти значения вычислялись при доступе к ключам:

from mysettings import settings # settings is only "prepared"

print settings['expensive1'] # Now the value is really computed.

Это возможно? Как?


  • проблема в том, что если вы сохраните свой модуль как есть, from mysettings import settings оценивает содержимое модуля и, следовательно, полностью создает dict. 21.05.2013

Ответы:


1

Если вы не отделяете аргументы от вызываемого, я не думаю, что это возможно. Однако это должно работать:

class MySettingsDict(dict):

    def __getitem__(self, item):
        function, arg = dict.__getitem__(self, item)
        return function(arg)


def expensive_to_compute(arg):
    return arg * 3

И сейчас:

>>> settings = MySettingsDict({
'expensive1': (expensive_to_compute, 1),
'expensive2': (expensive_to_compute, 2),
})
>>> settings['expensive1']
3
>>> settings['expensive2']
6

Редактировать:

Вы также можете кэшировать результаты expensive_to_compute, если к ним нужно обращаться несколько раз. Что-то вроде этого

class MySettingsDict(dict):

    def __getitem__(self, item):
        value = dict.__getitem__(self, item)
        if not isinstance(value, int):
            function, arg = value
            value = function(arg)
            dict.__setitem__(self, item, value)
        return value

И сейчас:

>>> settings.values()
dict_values([(<function expensive_to_compute at 0x9b0a62c>, 2),
(<function expensive_to_compute at 0x9b0a62c>, 1)])
>>> settings['expensive1']
3
>>> settings.values()
dict_values([(<function expensive_to_compute at 0x9b0a62c>, 2), 3])

Вы также можете переопределить другие методы dict в зависимости от того, как вы хотите использовать dict.

21.05.2013
  • Хранить функцию и перезаписывать __getitem__ — это умно, хотя я думаю, что было бы лучше наследовать abc.Mapping вместо встроенного dict. В противном случае он не поддерживает .get(). Вы можете проверить мой пример здесь gist.github.com/ligyxy/9b50bb8537069b4e154fec41a4b5995a 10.11.2017

  • 2

    Не наследовать встроенный dict. Даже если вы перезапишете метод dict.__getitem__(), dict.get() не будет работать так, как вы ожидали.

    Правильный путь — наследовать abc.Mapping от collections.

    from collections.abc import Mapping
    
    class LazyDict(Mapping):
        def __init__(self, *args, **kw):
            self._raw_dict = dict(*args, **kw)
    
        def __getitem__(self, key):
            func, arg = self._raw_dict.__getitem__(key)
            return func(arg)
    
        def __iter__(self):
            return iter(self._raw_dict)
    
        def __len__(self):
            return len(self._raw_dict)
    

    Затем вы можете сделать:

    settings = LazyDict({
        'expensive1': (expensive_to_compute, 1),
        'expensive2': (expensive_to_compute, 2),
    })
    

    Я также перечисляю примеры кода и примеры здесь: https://gist.github.com/gyli/9b50bb8537069b4e154fec41a4b5995a

    09.11.2017
  • В чем преимущество наследования abc.Mapping и переопределения __iter__() и __len__() по сравнению с наследованием dict и переопределением только get()? 20.11.2020

  • 3

    Храните ссылки на функции как значения для ключей, т.е.:

    def A():
        return "that took ages"
    def B():
        return "that took for-ever"
    settings = {
        "A": A,
        "B": B,
    }
    
    print(settings["A"]())
    

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

    import types
    class LazyDict(dict):
        def __getitem__(self,key):
            item = dict.__getitem__(self,key)
            if isinstance(item,types.FunctionType):
                return item()
            else:
                return item
    

    Применение:

    settings = LazyDict([("A",A),("B",B)])
    print(settings["A"])
    >>> 
    that took ages
    
    21.05.2013

    4

    Вы можете сделать expensive_to_compute функцией генератора:

    settings = {
        'expensive1' : expensive_to_compute(1),
        'expensive2' : expensive_to_compute(2),
    }
    

    Затем попробуйте:

    from mysettings import settings
    
    print next(settings['expensive1'])
    
    21.05.2013
  • Интересная идея, но не то, что я ищу. Я бы очень хотел сохранить словарь API нетронутым. 21.05.2013

  • 5

    Я бы заполнил значения словаря вызываемыми объектами и изменил их на результат при чтении.

    class LazyDict(dict):
        def __getitem__(self, k):
            v = super().__getitem__(k)
            if callable(v):
                v = v()
                super().__setitem__(k, v)
            return v
    
        def get(self, k, default=None):
            if k in self:
                return self.__getitem__(k)
            return default
    

    Затем с

    def expensive_to_compute(arg):
        print('Doing heavy stuff')
        return arg * 3
    

    ты можешь сделать:

    >>> settings = LazyDict({
        'expensive1': lambda: expensive_to_compute(1),
        'expensive2': lambda: expensive_to_compute(2),
    })
    
    >>> settings.__repr__()
    "{'expensive1': <function <lambda> at 0x000001A0BA2B8EA0>, 'expensive2': <function <lambda> at 0x000001A0BA2B8F28>}"
    
    >>> settings['expensive1']
    Doing heavy stuff
    3
    
    >>> settings.get('expensive2')
    Doing heavy stuff
    6
    
    >>> settings.__repr__()
    "{'expensive1': 3, 'expensive2': 6}"
    
    15.04.2020

    6

    Недавно мне нужно было что-то подобное. Смешивание обеих стратегий из Guangyang Li и michaelmeyer, вот как я это сделал:

    class LazyDict(MutableMapping):
      """Lazily evaluated dictionary."""
    
      function = None
    
      def __init__(self, *args, **kargs):
        self._dict = dict(*args, **kargs)
    
      def __getitem__(self, key):
          """Evaluate value."""
          value = self._dict[key]
          if not isinstance(value, ccData):
              value = self.function(value)
          self._dict[key] = value
          return value
    
      def __setitem__(self, key, value):
          """Store value lazily."""
          self._dict[key] = value
    
      def __delitem__(self, key):
          """Delete value."""
          return self._dict[key]
    
      def __iter__(self):
          """Iterate over dictionary."""
          return iter(self._dict)
    
      def __len__(self):
          """Evaluate size of dictionary."""
          return len(self._dict)
    

    Давайте лениво оценим следующую функцию:

    def expensive_to_compute(arg):
      return arg * 3
    

    Преимущество заключается в том, что функция еще не определена внутри объекта, а аргументы фактически сохранены (это то, что мне нужно):

    >>> settings = LazyDict({'expensive1': 1, 'expensive2': 2})
    >>> settings.function = expensive_to_compute # function unknown until now!
    >>> settings['expensive1']
    3
    >>> settings['expensive2']
    6
    

    Этот подход работает только с одной функцией.

    Могу отметить следующие преимущества:

    • реализует полный MutableMapping API
    • если ваша функция недетерминирована, вы можете сбросить значение для повторной оценки
    10.04.2020

    7

    передайте функцию для генерации значений первого атрибута get:

    class LazyDict(dict):
      """ Fill in the values of a dict at first access """
      def __init__(self, fn, *args, **kwargs):
        self._fn = fn
        self._fn_args = args or []
        self._fn_kwargs = kwargs or {}
        return super(LazyDict, self).__init__()
      def _fn_populate(self):
        if self._fn:
          self._fn(self, *self._fn_args, **self._fn_kwargs)
          self._fn = self._fn_args = self._fn_kwargs = None
      def __getattribute__(self, name):
        if not name.startswith('_fn'):
          self._fn_populate()
        return super(LazyDict, self).__getattribute__(name)
      def __getitem__(self, item):
        self._fn_populate()
        return super(LazyDict, self).__getitem__(item)
    
    
    
    >>> def _fn(self, val):
    ...   print 'lazy loading'
    ...   self['foo'] = val
    ... 
    >>> d = LazyDict(_fn, 'bar')
    >>> d
    {}
    >>> d['foo']
    lazy loading
    'bar'
    >>> 
    
    24.07.2020

    8

    В качестве альтернативы можно использовать пакет LazyDictionary, который создает потокобезопасный ленивый словарь.

    Монтаж:

    pip install lazydict
    

    Применение:

    from lazydict import LazyDictionary
    import tempfile
    lazy = LazyDictionary()
    lazy['temp'] = lambda: tempfile.mkdtemp()
    
    15.04.2021
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

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

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..