10

A purefunction - функция, аналогичная математической функции, где нет взаимодействия с «реальным миром» и побочными эффектами. С более практической точки зрения, это означает, что чистая функция может не:Как проверить, является ли функция чистой в Python?

  • печати или иным образом показать сообщение
  • случайные
  • зависит от времени системы
  • Изменить глобальные переменные
  • И другие

Все эти ограничения облегчают рассуждение о чистой функции чем нечистые. Большинство функций должны быть чистыми, чтобы программа могла иметь меньше ошибок.

На языках с огромной системой типов, такой как 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 

Однако он чувствует, как странный рубить, которые могут или не могут работать в зависимости от вашей удачи, могли бы вы мне помочь в написании лучшего декоратора?

+3

Вы можете «проверить.getsource», затем «ast.parse» и пройти узлы, проверить разные вещи ... но вы будете идти против причины, по которой существует язык, - посмотрите, используя модуль 'abc', если хотите , а затем 'isinstance', где нужно ... - python ** сильно ** напечатан - не ** статически ** напечатано –

+0

@ Динамические языки jonClements на самом деле выполняют меньше проверки времени компиляции, но я думаю, что это конкретная проверка значительно улучшит организацию программы и дважды проверит программистов на понимание его собственной работы. – Caridorc

+4

Затем используйте статически типизированный язык ... :) Вы можете либо просмотреть его как * плохую вещь, либо вещь * хорошо *, но так оно и есть –

ответ

9

Я как бы вижу, откуда вы родом, но я не думаю, что это может сработать.Возьмем простой пример:

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.

0

(не ответ, но слишком долго для комментария)

Таким образом, если функция может возвращать разные значения для того же набора аргументов, это не чисто?

Помните, что функции в 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(). Глобальные переменные могут меняться где-то еще, что влияет на чистоту вашей функции.

Все описанные выше ситуации могут быть трудными для обнаружения во время выполнения.

+0

Интересная идея, но я могу думать о многих функциях, которые не чисты, что может показаться слишком короткими временными шкалами, такими как получение часа дня, номер версии o/s, текущая ветвь git и т. д. – wallyk