Запоминание, когда аргументы могут быть очень большими
Предположим, у меня есть ссылочно прозрачная функция. Это очень легко запомнить; например :
def memoize(obj): memo = {} @functools.wraps(obj) def memoizer(*args, **kwargs): combined_args = args + (kwd_mark,) + tuple(sorted(kwargs.items())) if combined_args not in memo: memo[combined_args] = obj(*args, **kwargs) return cache[combined_args] return memoizer @memoize def my_function(data, alpha, beta): # ...
Теперь предположим, что аргумент data
my_function
огромен; скажем, это frozenset
с миллионами элементов. В этом случае стоимость memoization является запретительной: каждый раз нам приходилось вычислять hash(data)
как часть поиска словаря.
Я могу сделать словарь memo
атрибутом для data
вместо объекта внутри memoize
decorator. Таким образом, я могу полностью пропустить аргумент data
при выполнении поиска в кеше, так как вероятность того, что еще один огромный frozenset
будет одинаковой, незначительна. Однако этот подход заканчивает загрязнение аргумента, переданного my_function
. Хуже того, если у меня есть два или более больших аргумента, это не поможет вообще (я могу только прикрепить memo
к одному аргументу).
Есть ли что-нибудь еще, что можно сделать?
- python: словарь dilemma: как правильно индексировать объекты на основе атрибута
- плюсы и минусы использования заводского или обычного конструктора
- Схема проектирования, используемая в проектах
- Единый принцип ответственности для операций CRUD против коллекции
- Программирование на Python – Правила / Советы по разработке программного обеспечения на уровне предприятия на Python?
Ну, вы можете использовать «хэш» там без страхов. Хеш фенизета не рассчитывается более одного раза Python – только когда он создан – проверьте тайминги:
>>> timeit("frozenset(a)", "a=range(100)") 3.26825213432312 >>> timeit("hash(a)", "a=frozenset(range(100))") 0.08160710334777832 >>> timeit("(lambda x:x)(a)", "a=hash(frozenset(range(100)))") 0.1994171142578125
Не забывайте, что встроенный «хэш» Python __hash__
метод __hash__
объекта, который имеет свое возвращаемое значение, определенное во время создания встроенных hasheable объектов. Выше вы можете видеть, что вызов функции лямбда-идентификатора более чем в два раза медленнее, чем вызов хеша (a) "
Итак, если все ваши аргументы доступны, просто добавьте их хэш при создании «shared_args» – иначе просто напишите его создание, чтобы вы использовали хэш для файлов frozenset (и, возможно, других), с условным.
Оказывается, встроенный __hash__
не так уж плох, так как он кэширует свое значение после первого вычисления. Настоящий удар производительности происходит от встроенного __eq__
, поскольку он не __eq__
на одинаковых объектах и фактически проходит через полное сравнение каждый раз, что делает его очень дорогостоящим.
Один из подходов, о котором я думал, заключается в подклассе встроенного класса для всех больших аргументов:
class MyFrozenSet(frozenset): __eq__ = lambda self, other : id(self) == id(other) __hash__ = lambda self : id(self)
Таким образом, поиск в словарях будет мгновенным. Но равенство для нового класса будет нарушено.
Лучшее решение, вероятно, таково: только когда выполняется поиск словаря, большие аргументы могут быть обернуты внутри специального класса, который переопределяет __eq__
и __hash__
для возврата id()
обернутого объекта id()
. Очевидная реализация обертки немного раздражает, так как она требует копирования всех стандартных методов frozenset
. Возможно, извлечение его из соответствующего класса ABC
может облегчить его.
- Python: как создать контейнер с элементами, которые должны ссылаться на их контейнер
- Круговые проблемы импорта с приложениями Django, которые имеют зависимости друг от друга
- Как создать общий метод для методов с одинаковой структурой?
- Стандарты Python MixIn
- Специализация конструктора в python
- Использование метода класса __new__ в качестве фабрики: __init__ вызывается дважды
- Какова правильная модель в Python для реализации ленивых геттеров?
- Можно ли перенести основной цикл программы из графического интерфейса?
- Как включить макросы в Python?