另一种选择是非常重的——在函数对象中存储“默认参数值”作为代码的“重击”,以便在每次调用函数时都在没有为该参数指定值的情况下一遍又一遍地执行——并且会使获得早期绑定(在定义时绑定)变得更加困难,这通常是您所希望的。例如,在Python中:def ack(m, n, _memo={}):
key = m, n
if key not in _memo:
if m==0: v = n + 1
elif n==0: v = ack(m-1, 1)
else: v = ack(m-1, ack(m, n-1))
_memo[key] = v
return _memo[key]
……写一个像上面这样的记忆函数是一个相当基本的任务。同样地:for i in range(len(buttons)):
buttons[i].onclick(lambda i=i: say('button %s', i))
…简单的i=i,依赖于默认arg值的早期绑定(定义时间),是获得早期绑定的简单方法。因此,当前规则简单、直接,并且允许您以极易解释和理解的方式执行所有您想要的操作:如果您想要延迟绑定表达式的值,请在函数体中计算该表达式;如果您想要提前绑定,请将其计算为arg的默认值。
另一种选择是,在这两种情况下都强制延迟绑定,这并不能提供这种灵活性,并且会迫使您在每次需要提前绑定时都要经历一系列困难(比如将您的函数包装到一个闭包工厂中),就像上面的例子一样——这个假设性的设计决策(除了生成和反复评估到处都是重击的“看不见的”决策)给程序员带来了更沉重的样板。
换言之,“应该有一种,而且最好只有一种,显而易见的方法来实现它[1]:当您想要延迟绑定时,已经有一种非常明显的方法来实现它(因为所有函数的代码都只在调用时执行,显然所有被求值的东西都是延迟绑定的);让默认的arg求值产生早期绑定为您提供了一个明显的方法来实现早期绑定以及(一个加号!-)而不是给两个明显的方式得到后期绑定和没有明显的方式得到早期绑定(a减!-).
[1]:“不过,除非你是荷兰人,否则这种方式一开始可能并不明显。”