Python 函数头等对象

Python 函数头等对象,之前讲过Python函数是头等对象,也是包含一些属性的普通对象。关于函数对象可用的特殊方法,可以参阅Python语言参考手册。与获取对象属性的方法相同,通过__doc____name__属性可以获得函数的docstring和名称,还可以通过__code__属性获得函数体。在编译语言中,实现这类自省机制相对复杂,因为要考虑如何保留源代码信息,但对于Python来说这很简单。

函数可以赋给变量,可以作为参数传递,还可以作为其他函数的返回值。运用这些技术可以轻松编写高阶函数。

由于函数已经是对象了,所以Python具备函数式语言的许多要素。另外,由于函数是普通的对象,可以通过可调用对象创建函数,甚至可以将可调用类视为高阶函数。在定义可调用对象的__init__()方法时要尽量避免在其中设置有状态的类变量。通常的做法是利用策略模式定义__init__()方法。

一个按照策略模式创建的类使用另一个对象为其提供算法,或者部分提供算法,这样就可以在运行时动态注入算法,而不必将具体逻辑固定在类内部。

在可调用对象中嵌入策略对象的示例如下:

from typing import Callable
class Mersenne1:
    def __init__(self, algorithm: Callable[[int], int]) -> None:
        self.pow2 = algorithm
    def __call__(self, arg: int) ->int:
        return self.pow2(arg) - 1

这个类使用__init__()方法将另一个函数algorithm的引用保存在self.pow2中。上面没有创建任何有状态的变量,self.pow2不应发生变化。这里参数algorithm的类型标示是Callable[[int], int],表示该函数的输入和输出都是整型值。

这个函数根据策略对象提供的算法计算2的给定次方,可以作为类构造参数的3个备选算法如下所示:

def shifty(b: int) -> int:
    return 1 << b

def multy(b: int) -> int:
    if b == 0: return 1
    return 2 * multy(b - 1)

def faster(b: int) -> int:
    if b == 0: return 1
    if b % 2 == 1: return 2 * faster(b - 1)
    t = faster(b // 2)
    return t * t

shifty()函数通过左移位(bit)计算2的次方值。multy()函数利用简单的递归乘法计算。faster()函数利用分治策略,只需要执行 \log_2{(b)} 次乘法运算,而非b次。

以上3个函数的签名完全一样:Callable[[int], int],与Mersenne1.__init__(self)方法中的参数algorithm匹配。

下面就可以根据不同的算法创建 Mersennel 类的实例了,如下所示:

m1s = Mersenne1(shifty)
m1m = Mersenne1(multy)
m1f = Mersenne1(faster)

这样就利用不同的算法,定义不同的函数,计算得到了相同的结果。

 Python可以计算 M^{89}=2^{89}-1,这还未接近其嵌套递归深度限制。这是一个很大的质数,有27位。

赞(1)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

Python函数式编程

最新文章