Python 构建高阶函数

Python 构建高阶函数,可以通过创建Callable类对象来定义高阶函数。与编写生成器函数的思路类似,编写可调用对象是为了使用Python语句。除了能使用语句外,还可以在创建高阶函数时进行静态配置。

通过class声明定义Callable类对象,实际上定义了一个以函数为返回值的函数。通常可以使用可调用对象把两个函数组合起来,形成一个复杂函数。

如下面的类所示:

from typing import Callable, Optional, Any

class NullAware:
    def __init__(
            self, some_func: Callable[[Any], Any]) -> None:
        self.some_func = some_func
    def __call__(self, arg: Optional[Any]) -> Optional[Any]:
        return None if arg is None else self.some_func(arg)

这个类用于创建空值敏感的新函数。创建这个类的实例时需要提供一个函数(some_func)作为参数,对该函数的限制条件由Callable[[Any], Any]定义,即输入单一值并返回单一值。返回结果是可调用的,并接收一个可选参数。__call__()方法处理参数为None的情况,这个方法将返回结果类型定义为Callable[[Optional[Any]], Optional[Any]]

对表达式NullAware(math.log)求值,创建出一个作用于参数的新函数。__init__方法将用户定义的函数保存在结果对象中,该对象是个包含数据处理逻辑的函数。

通常会为生成的新函数指定一个名称,以便后续使用,如下所示:

null_log_scale = NullAware(math.log)

这里将新创建的函数赋给变量null_log_scale,接下来就可以在新的上下文中使用该函数了,如下所示:

>>> some_data = [10, 100, None, 50, 60]
>>> scaled = map(null_log_scale, some_data)
>>> list(scaled)
[2.302585092994046, 4.605170185988092, None, 3.912023005428146,
4.0943445622221]

或者像下面这样,把创建和使用函数写在同一个表达式中:

>>> scaled = map(NullAware(math.log), some_data)
>>> list(scaled)
[2.302585092994046, 4.605170185988092, None, 3.912023005428146,
4.0943445622221]

NullAware(math.log)求值的返回结果是一个匿名函数,该函数在map()函数中用于处理可迭代对象some_data

上面例子中的__call__()方法完全基于表达式求值,是一种简洁易用的基于底层组件函数创建复合函数的方法。当使用标量函数时,只需考虑很少几个设计约束就可以了,而当涉及可迭代集合时,就需要仔细斟酌了。

确保正确的函数式设计

使用无状态的函数式Python代码时,要注意使用对象,因为对象是有状态的。实际上,面向对象编程旨在把状态变化封装到类定义中。这样在使用Python类定义处理集合时,你会发现函数式编程和命令式编程背道而驰。

使用可调用对象创建复合函数,让我们能以相对简单的语法使用这些复合函数。在使用可迭代映射或者归约时,需要注意引入有状态对象的原因及方式。

回到之前的sum_filter_f()复合函数。基于Callable类定义的实现如下:

from typing import Callable, Iterable

class Sum_Filter:
    __slots__ = ["filter", "function"]
    def __init__(self,
            filter: Callable[[Any], bool],
            func: Callable[[Any], float]) -> None:
        self.filter = filter
        self.function = func
    def __call__(self, iterable: Iterable) -> float:
        return sum(
            self.function(x)
            for x in iterable
                if self.filter(x)
        )

这个类的每个对象只能有两个属性,以降低后续把该函数当成有状态对象使用的可能性。这种限制并不能完全杜绝对返回结果对象的修改,但将对象属性限制为两个,使得添加其他属性将引发异常。

初始化方法__init__()为对象实例加载了两个函数:filterfunc__call__()方法的返回值是一个生成器表达式,这个生成器表达式又使用了前两个函数。其中self.filter()方法用于对集合元素进行取舍,self.function()函数用于转换通过了filter()函数检验的元素。

类的实例是包含两个策略函数的函数。创建类的实例如下所示:

count_not_none = Sum_Filter(
    lambda x: x is not None,
    lambda x: 1)

该函数的功能是计算序列中非None元素的个数。它使用了两个匿名函数:一个过滤出序列中的非None元素,另一个将所有非None元素转换为1。

总的说来,这个count_not_none()函数与其他Python函数一样,使用方法比前面的sum_filter_f()函数简单一些。

如下所示使用count_not_none()函数:

N = count_not_none(data)

如下所示使用sum_filter_f()函数:

N = sum_filter_f(valid, count_, data)

可以看出,基于一个可调用对象的count_not_none()函数比普通函数的参数要少,所以使用起来相对简单。但它定义函数行为的代码分散在两处(可调用类中的定义,以及使用函数时指定的参数),从这个角度看,这种方式似乎不够清晰明了。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程