使用类型提示区分 Python 中的纯函数和不纯函数

Distinguish Pure and Impure functions in Python using Type Hints

有没有办法使用类型提示来显式表示纯函数(即没有副作用的函数)?

考虑以下示例,相同的功能,一个没有副作用,另一个有。

def add_two_pure(numbers: list[float]) -> list[float]:
    return [x + 2 for x in numbers]

numbers = [1.5, 2]
add_two_pure(numbers)
assert numbers == [1.5, 2]


# This is a bad practice, but it is just for illustration
def add_two_impure(numbers: list[float]) -> list[float]:
    for index, value in enumerate(numbers):
        numbers[index] = value + 2
    return numbers

numbers = [1.5, 2]
add_two_impure(numbers)
assert numbers == [3.5, 4]

我的第一个猜测是使用类似 Final 的东西,但 Pylance 抱怨“在此上下文中不允许使用“Final””:

from typing import Final

# Invalid Context
def add_two_pure(numbers: Final[list[float]]) -> list[float]:
    ...

有什么方法可以使这种区分更加明确吗?或者是否有任何 PEP 已经在讨论这个?

这是一个概念性问题,不需要特定的 Python 版本。

看起来没有,反正 MyPy 也没有。看到这个问题:https://github.com/python/mypy/issues/4468

Python还真没有纯函数的概念

您只能传递不可变对象以确保它们不会被更改。例如冻结集、元组或原始值。

或者,您可以编写自己的装饰器来断言它们不会更改您的变量。例如

def purity_check(func):
    def assert_purity(*args, **kwargs):
        hash_or_else = lambda x: hash(x) if isinstance(x, Hashable) else hash(str(x))
        original_ids = list(map(hash_or_else, [*args, *kwargs.values()]))
        func(*args, **kwargs)
        assert all(
            [
                a == b
                for a, b in zip(
                    list(map(hash_or_else, [*args, *kwargs.values()])), original_ids
                )
            ]
        ), f"{func.__name__} is not pure"

    return assert_purity

正如@Charles Duffy 提到的,您永远无法知道它是否有其他副作用,例如 global 关键字、网络请求、os-level 更改等。您必须使用静态分析那。