Я реализую функцию с двумя аргументами и кэширую результат для повышения производительности (техника «мемоизации»). Но я вижу дублирование функции кеширования. Например:
@memoize
def fn(a,b):
#do something.
Проблема в том, что я заранее знаю, что fn(a,b) == fn(b,a), поэтому я хотел бы, чтобы он понимал, что кортеж аргументов (a,b) в этом случае эквивалентен (b,a), но эта функция в настоящее время кэширует их как две отдельные записи.
Нужно ли мне создавать для этой цели новый класс или есть другой более элегантный способ? Хотя я знаю, что могу кэшировать одно и то же возвращаемое значение функции для обоих кортежей, но мне действительно хотелось бы знать, как реализовать кортежи, не зависящие от индекса.
Этот код мемоизации является просто примером, я хочу, чтобы мой объект кортежа был чрезвычайно универсальным, который можно было бы использовать и в других ситуациях.
Или tuple(sorted(argument_tuple))
Если вы не хотите делать его очень общим, вы можете одновременно кэшировать (a, b) и (b, a). Таким образом, вы вызываете функцию один раз, когда, например, fn(1, 2) вызывается, при этом вы также предварительно кешируете на случай, если fn(2, 1) когда-либо будет вызван.
@jonrsharpe Мне бы очень хотелось, чтобы это было чрезвычайно универсальным, эта мемоизация - всего лишь пример из моей головы.
Позволяет ли украшение @memoize указать, как вычисляется ключ кеша?
@Barmar. Я бы предпочел возиться только с двумя кортежами, с которыми имею дело. Я не хочу вносить никаких изменений в какой-либо другой код. Просто хочу сделать этот кортеж универсальным.






Если вам нужно чрезвычайно общее решение, не только для двух параметров, но, возможно, для нескольких, где (a, b, a) обрабатывается так же, как (a, a, b), но отличается от (a, b, b), вы можете использовать frozenset(Counter(argument_tuple).items()) для подсчета количества вхождений каждого значения и поворота полученного dict в замороженный набор кортежей (после from collections import Counter).
Вы можете разделить его на две функции. Открытая функция принимает аргументы в любом порядке и вызывает внутреннюю функцию с отсортированными аргументами. Внутренняя функция - это функция с кешем.
def fn(*args):
return cached_fn(*sorted(args))
@memoize
def cached_fn(*args)
# do something
Это компромисс между пространством и временем - есть несколько дополнительных вызовов функций, чтобы уменьшить размер кеша вдвое. Надеюсь, у вас нет функций с действительно длинными списками аргументов, поэтому сортировка не должна добавлять слишком много накладных расходов.
Это решение требует, чтобы тип данных аргументов имел функцию сравнения, чтобы их можно было отсортировать. Я не могу придумать способ написать это в общих чертах без такой предпосылки. Имея дело с определенными типами, вы можете разработать собственный метод канонизации порядка. Или вы можете использовать свою собственную функцию хеширования в кеше и сделать ее независимой от порядка.
спасибо, но как я могу сделать это универсальным, а не конкретным для этого случая. как тот, на который ответил @blhsing, но, может быть, немного проще ??
Я обобщил видимую функцию
Вы имеете в виду *sorted(args)? У кортежей нет этого метода.
Было ощущение, что я, возможно, понял это задом наперед.
Имейте в виду, что это работает только в том случае, если объекты в списке аргументов можно сортировать / сравнивать, поэтому, вероятно, это не так универсально, как могло бы быть, если это то, что ищет OP. Также имейте в виду, что сортировка имеет более высокие временные затраты, чем подсчет, хотя это не будет иметь большого значения, если длина списка аргументов не будет длинной.
@blhsing Ты получил моего дрейфующего брата. Это именно то, что я хотел спросить.
@blhsing Хороший замечание, я добавил в ответ некоторое обсуждение этого требования. Я уже говорил о влиянии сортировки на производительность.
Простое решение - использовать
sorted(argument_tuple)илиfrozenset(argument_tuple)в качестве ключа кеша.