如何在Python中检查函数是否纯粹?

时间:2015-07-22 16:07:40

标签: python metaprogramming decorator purely-functional formal-verification

pure function是一个类似于数学函数的函数,其中没有与"真实世界"也没有副作用。从更实际的角度来看,这意味着纯函数可以

  • 打印或以其他方式显示消息
  • 随机
  • 取决于系统时间
  • 更改全局变量
  • 和其他人

所有这些限制使得理解纯函数比非纯函数更容易。然后,大多数函数应该是纯函数,以便程序可以减少错误。

在像Haskell这样的庞大类型系统的语言中,如果函数是纯粹的或者不是纯粹的,读者可以从一开始就知道,使得连续阅读更容易。

在Python中,这个信息可以通过放在函数顶部的@pure装饰器来模拟。我也希望那个装饰器实际上做一些验证工作。我的问题在于这种装饰器的实现。

现在,我只需查看该功能的源代码,如globalrandomprint等流行语,如果找到其中一个,则会抱怨。

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

然而,感觉就像一个奇怪的黑客,根据你的运气可能会或可能不会起作用,你能帮我写一个更好的装饰师吗?

2 个答案:

答案 0 :(得分: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)

这将表明所谓的纯粹的add函数实际上已经改变了RegisteredNumber类的状态。这不是一个愚蠢的例子:在我的生产代码库中,我们有跟踪每个创建实例的类,例如,允许通过密钥访问。

纯度的概念在Python中没有多大意义。

答案 1 :(得分: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()内置函数。全局变量可能会在其他地方发生变化,从而影响函数的纯度。

上述所有情况可能难以在运行时检测到。