Как проверить, является ли функция чистой в Python?
A pure function - это функция, аналогичная функции Математическая функция, где нет взаимодействия с "реальным миром" и побочными эффектами. С более практической точки зрения это означает, что чистая функция может не:
- Распечатайте или покажите сообщение другим пользователям
- Быть случайным
- В зависимости от системного времени
- Изменение глобальных переменных
- И другие
Все эти ограничения облегчают рассуждение о чистых функциях, чем нечистые. Большинство функций должны быть чистыми, чтобы программа могла иметь меньше ошибок.
В языках с огромной системой типов, такой как Haskell, читатель может знать с самого начала, если функция является или не является чистой, что облегчает последовательное чтение.
В Python эта информация может быть эмулирована декоратором @pure
, помещенным поверх функции. Я также хотел бы, чтобы этот декоратор действительно выполнял некоторые проверки. Моя проблема заключается в реализации такого декоратора.
Прямо сейчас я просто смотрю исходный код функции для ключевых слов, таких как global
или random
или print
, и жалуется, если он найдет один из них.
import inspect
def pure(function):
source = inspect.getsource(function)
for non_pure_indicator in ('random', 'time', 'input', 'print', 'global'):
if non_pure_indicator in source:
raise ValueError("The function {} is not pure as it uses `{}`".format(
function.__name__, non_pure_indicator))
return function
Однако это похоже на странный взлом, который может или не может работать в зависимости от вашей удачи, не могли бы вы помочь мне написать лучшего декоратора?
Ответы
Ответ 1
Я вижу, откуда вы родом, но я не думаю, что это может сработать. Возьмем простой пример:
def add(a,b):
return a + b
Итак, это, вероятно, выглядит "чистым" для вас. Но в Python +
здесь есть произвольная функция, которая может делать что угодно, только в зависимости от привязок, действующих при ее вызове. Так что a + b
может иметь произвольные побочные эффекты.
Но это еще хуже. Даже если это просто делает стандартное целое число +
, тогда происходит больше "нечистых" вещей.
+
создает новый объект. Теперь, если вы уверены, что только у вызывающего есть ссылка на этот новый объект, тогда есть смысл, в котором вы можете думать об этом как о чистой функции. Но вы не можете быть уверены, что во время процесса создания этого объекта ссылка на него не просочилась.
Например:
class RegisteredNumber(int):
numbers = []
def __new__(cls,*args,**kwargs):
self = int.__new__(cls,*args,**kwargs)
self.numbers.append(self)
return self
def __add__(self,other):
return RegisteredNumber(super().__add__(other))
c = RegisteredNumber(1) + 2
print(RegisteredNumber.numbers)
Это покажет, что предположительно чистая функция добавления фактически изменила состояние класса RegisteredNumber
. Это не тупо надуманный пример: в моей производственной кодовой базе у нас есть классы, которые отслеживают каждый созданный экземпляр, например, чтобы разрешить доступ с помощью ключа.
Понятие чистоты просто не имеет большого смысла в Python.
Ответ 2
(не ответ, но слишком длинный для комментария)
Итак, если функция может возвращать разные значения для одного и того же набора аргументов, она не является чистой?
Помните, что функции в Python являются объектами, поэтому вы хотите проверить чистоту объекта...
Возьмем этот пример:
def foo(x):
ret, foo.x = x*x+foo.x, foo.x+1
return ret
foo.x=0
вызов foo(3)
несколько раз дает:
>>> foo(3)
9
>>> foo(3)
10
>>> foo(3)
11
...
Кроме того, чтение глобалов не требует использования оператора global
или встроенного global()
внутри вашей функции. Глобальные переменные могут меняться в другом месте, влияя на чистоту вашей функции.
Все описанные выше ситуации могут быть трудными для обнаружения во время выполнения.