Хеширование функционального объекта в python

1

Позвольте мне объяснить, зачем мне это нужно:

Я разрабатываю библиотеку и нуждаюсь в сериализации функций python.

Вот две служебные функции, которые я использую:

def serialize_func(fn: function) -> Tuple[bytes, str]:
    return marshal.dumps(fn.__code__), fn.__name__


def deserialize_func(serialized_fn: Tuple[bytes, str]) -> function:
    return types.FunctionType(
        marshal.loads(serialized_fn[0]), globals(), serialized_fn[1]
    )

Теперь, если каким-то образом я могу кэшировать сериализованные функции и хранить их в таком dict:

Dict[function_hash, function]

{
     <function hash>: <function>,
     <function hash>: <function>,
     ...
}

Это приведет к значительному улучшению производительности, поскольку мне не нужно многократно сериализовывать (и переносить) их.

Есть ли надежный способ достичь этого?

EDIT: Если я попробовал хэшировать функцию, это не совсем то, что я имею в виду.

In [1]: def x():
   ...:     pass

In [2]: hash(x)
Out[2]: 8745212393041

In [3]: def x():
   ...:     pass

In [4]: hash(x)
Out[4]: -9223363291642382793

Мне нужно 2 функции с одним и тем же телом, сигнатурой, глобальной областью (и чем-то другим, чего я, вероятно, не хватает), чтобы вернуть тот же хеш.

Вот библиотека, о которой идет речь.


EDIT: Вот несколько подробных объяснений, почему я хочу это сделать.

Я отправляю функцию из процесса ("Клиент") на другой ("Сервер") и выполняю его на "Сервер".

Звучит странно и сложно, но у меня есть веская причина.

Весь этот процесс делает функцию строго атомной для всех "клиентов"

"Сервер" - это "Актер". Таким образом, одна и только одна функция может работать одновременно, что облегчает программисту избегать условий гонки.

Теперь, если "Клиент" уже отправил функцию на "Сервер" один раз, а затем для последующих взаимодействий, он отправляет только хэш этой функции, а "Сервер" может просто искать эту функцию в своей таблице.

  • 2
    Строки могут быть хэшируемыми (включая байтовые массивы, которые возвращает marshal.dumps ), поэтому они работают без каких-либо изменений в качестве ключей словаря. Хотя столкновение с хеш-значением встречается не часто, это вполне возможно, поэтому не рекомендуется использовать словарь, в котором ключом является хеш-значение.
  • 0
    Я хочу хешировать «функциональные» объекты, а не их сериализованную версию. Сериализация занимает некоторое конечное время, и это конечное время приводит к перфектной проблеме, когда выполняется многократно. Прямо сейчас я должен заставить пользователя сериализовать его один раз и использовать сериализованный аналог самостоятельно. ТАК искал, чтобы автоматизировать эту часть.
Показать ещё 10 комментариев
Теги:

1 ответ

1
Лучший ответ

Мне нужно 2 функции с одним и тем же телом, сигнатурой, глобальной областью (и чем-то другим, чего я, вероятно, не хватает), чтобы вернуть тот же хеш.

Тогда вам нужно будет построить хэш самостоятельно, исходя из этих критериев.

Например:

def foo(x):
    return "It is " + str(x + 1)

def bar(x):
    return "The answer is " + str(x + 2)

def quux(x):
    return "The answer is " + str(x - 2)


def fnhash(f):
    c = f.__code__
    return hash((c.co_argcount, c.co_code))

fnhash(foo)
# => -640999299468968616
fnhash(bar)
# => -640999299468968616
fnhash(quux)
# => -1235803056671018747

Обратите внимание, что первые одни и те же, потому что код и число позиционных параметров одинаковы - мы не включили co_consts, поэтому другая строка и изменение с 1 по 2 не учитываются. Обратите внимание, что последний отличается, потому что мы изменили операцию (которая повлияла на co_code, что в свою очередь повлияло на fnhash).

Вам будет нужно выбрать именно те атрибуты объекта кода, которые вы выберете, чтобы иметь смысл (например, я сомневаюсь, что вы захотите включить co_filename и co_firstline). См. Значения различных полей co_* в inspect документах.

  • 0
    Спасибо, это довольно полезно. Есть ли способ узнать, является ли глобальная область одинаковой? Например - gist.github.com/devxpy/da53556d1b5bc97a745c41bff5ff50d2
  • 0
    На самом деле, мне не нужно этого делать, поскольку я не передаю глобальную область, и это не имеет значения. Этого Много должно хватить. Благодарю.
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню