Как сделать кортеж из двух индексов?

Я реализую функцию с двумя аргументами и кэширую результат для повышения производительности (техника «мемоизации»). Но я вижу дублирование функции кеширования. Например:

@memoize
def fn(a,b):
    #do something.

Проблема в том, что я заранее знаю, что fn(a,b) == fn(b,a), поэтому я хотел бы, чтобы он понимал, что кортеж аргументов (a,b) в этом случае эквивалентен (b,a), но эта функция в настоящее время кэширует их как две отдельные записи.

Нужно ли мне создавать для этой цели новый класс или есть другой более элегантный способ? Хотя я знаю, что могу кэшировать одно и то же возвращаемое значение функции для обоих кортежей, но мне действительно хотелось бы знать, как реализовать кортежи, не зависящие от индекса.

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

Простое решение - использовать sorted(argument_tuple) или frozenset(argument_tuple) в качестве ключа кеша.

AKX 08.01.2019 18:52

Или tuple(sorted(argument_tuple))

Matthew Woodruff 08.01.2019 18:54

Если вы не хотите делать его очень общим, вы можете одновременно кэшировать (a, b) и (b, a). Таким образом, вы вызываете функцию один раз, когда, например, fn(1, 2) вызывается, при этом вы также предварительно кешируете на случай, если fn(2, 1) когда-либо будет вызван.

jonrsharpe 08.01.2019 18:55

@jonrsharpe Мне бы очень хотелось, чтобы это было чрезвычайно универсальным, эта мемоизация - всего лишь пример из моей головы.

Gsbansal10 08.01.2019 18:57

Позволяет ли украшение @memoize указать, как вычисляется ключ кеша?

Barmar 08.01.2019 18:57

@Barmar. Я бы предпочел возиться только с двумя кортежами, с которыми имею дело. Я не хочу вносить никаких изменений в какой-либо другой код. Просто хочу сделать этот кортеж универсальным.

Gsbansal10 08.01.2019 19:03
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
6
45
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если вам нужно чрезвычайно общее решение, не только для двух параметров, но, возможно, для нескольких, где (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, но, может быть, немного проще ??

Gsbansal10 08.01.2019 19:08

Я обобщил видимую функцию

Barmar 08.01.2019 19:11

Вы имеете в виду *sorted(args)? У кортежей нет этого метода.

jonrsharpe 08.01.2019 19:20

Было ощущение, что я, возможно, понял это задом наперед.

Barmar 08.01.2019 19:21

Имейте в виду, что это работает только в том случае, если объекты в списке аргументов можно сортировать / сравнивать, поэтому, вероятно, это не так универсально, как могло бы быть, если это то, что ищет OP. Также имейте в виду, что сортировка имеет более высокие временные затраты, чем подсчет, хотя это не будет иметь большого значения, если длина списка аргументов не будет длинной.

blhsing 08.01.2019 19:26

@blhsing Ты получил моего дрейфующего брата. Это именно то, что я хотел спросить.

Gsbansal10 08.01.2019 20:11

@blhsing Хороший замечание, я добавил в ответ некоторое обсуждение этого требования. Я уже говорил о влиянии сортировки на производительность.

Barmar 08.01.2019 20:26

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