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位。