An environment in which an expression is evaluated consists of a sequence of *frames*, depicted as boxes. Each frame contains *bindings*, each of which associates a name with its corresponding value.
对于变量或者常量,我们会 bindings 链接到具体内存中的值的空间。这很好理解,我们在对这样的 bound name 解析(evaluation,我不知道这中文应该叫什么)的时候,我们就是想要使用那样一个内存空间,读写它的值。
对于函数而言,我们处理 bindings 的方式更加复杂:对于函数而言,我们不仅像变量那样拥有一个在 environment 中的显式的 bound name 这样的名字,我们还拥有在深层的独属于每一个函数本身的一个 intrinsic name。虽然我们永远直接调用的都是 bound name,但是这样的名字并不能直接解析(evaluation),这样的名字如同一个指针,指向对于的函数的 intrinsic name,然后用 intrinsic name 去进行实际上的操作。值得注意的是,你可以在 environment 中创建大量的 bound name(你也许也可以叫做别名),但是对于一个实体存在的函数,只有一个 intrinsic name 与其对应。
当你新创建一个函数的时候,会同时创建 bound name 与 intrinsic name 并会建立 bound name 指向 intrinsic name 然后 intrinsic name 永久 bind(捆绑)函数本体的关系
同时你也可以解绑这样的关系,让一个 bound name 绑定上另一个实体,无论其是函数,数值,还是一个对象
更重要的一点,bound name 的实际解析结果(evaluation)取决于他所指向的内容,Python并没有直接给这样的别名严格划分(这也是Python弱类型的体验),所以,当你不清晰的混用这样的东西的时候,你很容易出现自己难以发现的 Bug
这是一个很常见的例子:
代码块
f = max
max = 3
result = f(2, 3, 4)
max(1, 2) # Causes an error
是否 intrinsic name 就直接对应函数呢?在很多现代语言,包括 Python 中,这是否定的。我们不仅以函数名来区分我们的函数,我们还通过函数的参数(parameters)的数量和类型(Python的类型非常不明确,这点很蛋疼)来区分。intrinsic name 也只会对应到我们一类同名的函数组,我们再通过传入参数与函数名称(这些信息的总和叫做Function Signatures)准确定位到想要使用的函数。而对于一些特殊的函数,叫做内置函数,例如max这样的函数,可以接受任意数量的参数,无论接受多少的参数这样的内置函数都表现为name(...),因为他们并没有被显式定义过,这样的函数过于特殊,作为Python解释器的一部分出现
environment(环境)在用户自定义函数中又是如何作用的呢?当调用某个自定函数时,我们将会创建新的域(frame),这个域(你可以看作)会在函数定义的那一帧创建一个域的分支,并且将函数的参数绑定到函数形式参数的名称上,所以对于当前的函数内的不仅包含当前内部的环境,还包括这个函数定义时那一帧的环境。我们又将环境分为多个 frame,我们在解析一个 name 的时候,我们会逐层向上找,找到的第一个与这个 name 绑定的在 environment 中的值拿来解析。我们找到了一个模型来解释我们的 name 将如何解析,但是这并不意味着任何实现,并不意味着解释器在实现的时候会真实的“寻找”,但这样的模型确实是准确的
另外一个原则在于,我们函数的功能并不受函数参数的 name 的影响,这样做的效果就是我们我们的参数在函数内部是局部的。实际上,这也是由于存在 frame 这样的机制决定的