Разработка эвристики для проверки простых анонимных функций Python на эквивалентность

Я знаю, как работает сравнение функций в Python 3 (просто сравнивая адрес в памяти), и понимаю почему.

Я также понимаю, что «настоящее» сравнение (возвращают ли функции fи gодин и тот же результат при одних и тех же аргументах для любых аргументов?) практически невозможно.

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

lambda x : x == lambda x : x # True
lambda x : 2 * x == lambda y : 2 * y # True
lambda x : 2 * x == lambda x : x * 2 # True or False is fine, but must be stable
lambda x : 2 * x == lambda x : x + x # True or False is fine, but must be stable

Обратите внимание, что я заинтересован в решении этой проблемы для анонимных функций ( lambda), но не стал бы не возражаете, если решение также работает для именованных функций.

Мотивация для этого заключается в том, что внутри модуля blistбыло бы неплохо проверить, что два экземпляра sortedsetимеют одинаковую функцию сортировки перед выполнением объединения и т. д. на них.

Именованные функции представляют меньший интерес, поскольку я могу предположить, что они различны, когда они не идентичны. В конце концов, предположим, что кто-то создал два sortedsets с именованной функцией в аргументе key. Если они намерены сделать эти экземпляры «совместимыми» для операций над множествами, они, вероятно, будут использовать одну и ту же функцию, а не две отдельные именованные функции, выполняющие идентичные операции.

Я могу придумать только три подхода. Все они кажутся сложными, поэтому любые идеи приветствуются.

  1. Сравнение байт-кодов может работать, но может раздражать, что оно зависит от реализации (и, следовательно, код, который работал на одном Python, ломается на другом).

  2. Сравнение токенизированного исходного кода кажется разумным и переносимым. Конечно, он менее мощный (поскольку одинаковые функции чаще отвергаются).

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

РЕДАКТИРОВАТЬ

Более сложный пример, основанный на комментарии @delnan:

# global variable
fields = ['id', 'name']

def my_function():
  global fields
  s1 = sortedset(key = lambda x : x[fields[0].lower()])
  # some intervening code here
  # ...
  s2 = sortedset(key = lambda x : x[fields[0].lower()])

Можно ли ожидать, что ключевые функции для s1и s2оцениваются как равные?

Если промежуточный код вообще содержит какой-либо вызов функции, значение полей может быть изменено, что приведет к различным функциям клавиш для s1и s2. Поскольку мы явно не будем проводить анализ потока управления для решения этой проблемы, ясно, что мы должны оценивать эти две лямбда-функции как разные, если мы пытаемся выполнить эту оценку перед выполнением. (Даже если бы fieldsне было глобальным, к нему могло бы быть привязано другое имя и т. д.) Это сильно урезало бы полезность всего этого упражнения, поскольку немногие лямбда-функции не зависели бы от окружающая обстановка.

РЕДАКТИРОВАТЬ 2:

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

В результате мне нужен не только байт-код, но и глобальный состояние на момент создания объекта функции (предположительно __globals__). Затем я должен сопоставить все переменные из внешней области со значениями из __globals__.

14
задан Josh Caswell 18 November 2013 в 22:13
поделиться