2013-11-18 2 views
9

Мне интересно, как я могу проверить, совпадают ли две функции. Примером может служить значение (lambda x: x) == (lambda y: y). Насколько я знаю, Python будет проверять, занимают ли функции одно и то же место в памяти, но не имеют ли они одну и ту же операцию. Я знаю, что это непрактично, чтобы иметь такую ​​функциональность.Проверьте, равны ли две функции Python

Другим решением может быть некоторый метод, который я могу использовать для функции, чтобы увидеть, что она содержит или как она работает. Так что вроде (lambda x: x).what(), который вернет способ работы, может быть, в словаре или что-то в этом роде.

Мне понравился бы ответ, но я сомневаюсь, что это возможно.

+2

Тесно связанный: [Разработка эвристики для проверки простых анонимных функций Python для эквивалентности] (http://stackoverflow.com/q/9963155), [Clojure test for equal of function expression] (http://stackoverflow.com/q/9393713) –

+0

... хотя ответы на вопрос Python, похоже, предполагают, что невозможно получить байт-код напрямую, и поэтому вам нужно его разобрать, а затем попытаться удалить все лишние вещи, которые дизассемблер добавил из других источников, кроме байт-код, который является глупым, когда на самом деле вполне возможно (и намного проще) просто получить байт-код напрямую ... – abarnert

+1

В случае, если кто-то еще пришел сюда, чтобы узнать, как сравнить две ссылки на одну и ту же функцию: 'f == g' работает для меня – lucidbrot

ответ

6

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

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


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


Но то, о чем вы просили, отличается от любого из них; вы, очевидно, хотите просмотреть код вручную, чтобы увидеть «как это работает»:

Другим решением может быть некоторый метод, который я могу использовать для функции, чтобы увидеть, что она содержит, и как она работает. Таким образом (lambda x: x) .what(), который вернет способ работы метода, может быть, в словаре или что-то в этом роде.

Эта функция уже существует: dis.dis. Когда вы запускаете его для функции, он сообщает вам, как работает эта функция. Не в словаре (словаре чего?), А в последовательности строк байт-кода для интерпретатора Python (который является относительно простой стековой машиной с добавлением более высокоуровневого материала сверху, в основном описанным здесь, в документах dis) ,

Или, проще говоря, вы можете получить исходное сообщение с inspect.getsource.

Вот что два похожи с примерами:

>>> f1 = lambda x: x 
>>> f2 = lambda y: y 
>>> def f3(z): 
...  return z 
>>> dis.dis(f1) 
    1   0 LOAD_FAST    0 (x) 
       3 RETURN_VALUE 
>>> dis.dis(f2) 
    1   0 LOAD_FAST    0 (y) 
       3 RETURN_VALUE 
>>> dis.dis(f3) 
    1   0 LOAD_FAST    0 (z) 
       3 RETURN_VALUE 
>>> inspect.getsource(f1) 
'f1 = lambda x: x\n' 
>>> inspect.getsource(f2) 
'f2 = lambda y: y\n' 
>>> inspect.getsource(f3) 
'def f3(z):\n return z\n' 

В первом случае, вы должны знать достаточно о dis, чтобы понять, что (x) и т.д., не являются частью байткод, но скорее часть списка локальных имен функции. (Это объясняется так же, как и в документах inspect, как в документах dis.) Во-вторых, вам нужно знать достаточно о Python, чтобы понять, что def и lambda определяют ту же самую функцию. Итак, в любом случае, нет никакого способа для автоматизировать это (или, действительно, что-нибудь многое, кроме ответа Martijn).

23

Единственное, что вы можете проверить это объектный код равенства:

>>> x = lambda x: x 
>>> y = lambda y: y 
>>> x.__code__.co_code 
'|\x00\x00S' 
>>> x.__code__.co_code == y.__code__.co_code 
True 

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

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

+0

Вы также можете создавать объекты, где ваш тест верен, но результат отличается. (Это немного надуманно, но что угодно ...): https://gist.github.com/sharth/7536465 –

+0

@sharth: И ответ уже охватывает это: «Вам, возможно, потребуется проверить ...» Он дал замыкания как один пример. Атрибуты пользовательских функций - это другие. Предоставление функции другой '__globals__' является другой. Я не думаю, что нам нужен исчерпывающий список. – abarnert

Смежные вопросы