<p>首先:Python函数对象是<em>一类对象</em>。一条<code>def</code>语句将生成一个新的函数对象,您可以使用函数名检索该对象:</p>
<pre><code>>>> def foo():
... return 'foo was called'
...
>>> foo
<function foo at 0x11b3d8ae8>
</code></pre>
<p>这意味着您也可以将该对象分配给其他名称,并且可以将它们作为参数传递给函数调用。然后,您可以稍后通过向引用添加<code>(...)</code>来调用函数对象:</p>
^{pr2}$
<p>函数名在当前命名空间中分配给。在模块中,这是全局变量,但在<code>cons</code>这样的函数中,名称作为局部名称添加。<code>return pair</code>函数中的{<cd4>}然后将函数对象{<cd6>}返回给调用者。在</p>
<p>而函数参数,如<code>f</code>和<code>a</code>和<code>b</code>也是变量;如果将函数对象作为参数传入,则<code>parameter_name(...)</code>将调用<code>paramater_name</code>,并传入<code>...</code>部分中的任何参数。<code>f(a, b)</code>调用<code>f</code>,并传入<code>a</code>和{<cd9>}。在</p>
<p>下一个要理解的项目是<a href="https://en.wikipedia.org/wiki/Closure_(computer_programming)" rel="noreferrer"><em>closures</em></a>;闭包是附加到函数对象的额外名称空间,用于来自周围范围的变量。在</p>
<p>在下面的示例中,<code>spam</code>是<code>bar</code>函数闭包中的一个名称:</p>
<pre><code>>>> def foo():
... spam = 'Vikings'
... def bar():
... return spam
... return bar
...
>>> foo()
<function foo.<locals>.bar at 0x11b44bf28>
>>> foo()()
'Vikings'
</code></pre>
<p>调用<code>foo()</code>返回一个新的函数对象,<code>bar()</code>函数位于<code>foo()</code>内。调用返回的函数对象生成<code>'Vikings'</code>,即<code>foo</code>函数中<code>spam</code>变量的值。<code>bar()</code>是如何获得访问权限的?通过关闭:</p>
<pre><code>>>> foo().__closure__
(<cell at 0x11b3c05b8: str object at 0x11b469180>,)
>>> foo().__closure__[0].cell_contents
'Vikings'
</code></pre>
<p>因此,嵌套函数可以通过闭包访问周围范围内的名称。(旁注:存储在闭包中的不是<em>值</em>,而是<em>变量</em>。变量可以随时间而变化,就像以后访问名称的其他变量一样,将反映新值;如果<code>spam</code>稍后被更改,再次调用<code>bar()</code>将返回新值)。在</p>
<p>现在到您的函数:<code>cons()</code>创建一个内部函数<code>pair()</code>,并且<code>pair()</code>可以通过其闭包访问参数<code>a</code>和{<cd9>}:</p>
<pre><code>>>> def cons(a, b):
... def pair(f):
... return f(a, b)
... return pair
...
>>> cons(42, 81)
<function cons.<locals>.pair at 0x11b46f048>
>>> pair_42_81 = cons(42, 81)
>>> pair_42_81.__closure__
(<cell at 0x11b3c02b8: int object at 0x10f59a750>, <cell at 0x11b3c05b8: int object at 0x10f59ac30>)
>>> pair_42_81.__closure__[0].cell_contents
42
>>> pair_42_81.__closure__[1].cell_contents
81
</code></pre>
<p><code>pair()</code>函数接受一个参数<code>f</code>,然后<em>调用该参数,传入<code>a</code>和{<cd9>}。让我们看看当我们传入<code>print</code>时会发生什么。在</p>
<p><code>print</code>也是一个函数,它是一个可以调用的对象,它将参数写入控制台,中间有空格:</p>
<pre><code>>>> print
<built-in function print>
>>> print('arg1', 'arg2')
arg1 arg2
</code></pre>
<p>如果将其传递给<code>pair()</code>返回的<code>pair()</code>函数,则可以看到<code>f(a, b)</code>的作用:</p>
<pre><code>>>> pair_42_81(print)
42 81
</code></pre>
<p>传递给<code>print</code>,被分配给<code>f</code>,而{<cd13>}与{<cd46>}完全相同。在</p>
<p>我们可以看到,调用了<code>print()</code>,因为这些值被写入控制台。但也可以创建一个返回新值的函数。假设有一个函数将两个数字相加并返回该值:</p>
<pre><code>>>> def add(first, second):
... return first + second
...
>>> add(42, 81)
123
>>> pair_42_81(add)
123
</code></pre>
<p>我们可以直接调用函数,并返回<code>123</code>,也可以让<code>pair_42_81()</code>为我们执行,并返回相同的结果。简单!在</p>
<p>所有这些都是有效的,因为函数是对象,可以像其他变量一样传递,而且<code>pair_42_81</code>在闭包中存储了<code>a = 42</code>和{<cd52>},并将使用这两个参数调用给定的对象<code>f</code>。在</p>
<p>接下来是<code>cdr()</code>和<code>car()</code>,它们将返回对中的第一个或最后一个元素。如果<code>cons(a, b)</code>产生{<cd57>}返回{<cd13>},那么<code>cdr()</code>和{<cd55>}必须各自创建一个函数来传递给<code>pair()</code>,该函数将提取<code>a</code>或{<cd9>}。在</p>
<p>因此在每个函数中创建一个嵌套函数,并使用该函数使<code>cdr()</code>和{<cd55>}调用<code>pair()</code>。嵌套函数执行选择<code>a</code>或{<cd9>}的工作,并返回该值。然后将调用结果返回给外部世界:</p>
<pre><code>def car(pair):
def return_first(a, b):
return a
return pair(return_first)
def cdr(pair):
def return_last(a, b):
return b
return pair(return_last)
</code></pre>
<p><code>pair(return_first)</code>调用<code>return_first(a, b)</code>,<code>a</code>被返回,<code>car()</code>可以将其返回给被调用方右侧:</p>
<pre><code>>>> car(cons(42, 81))
42
</code></pre>
<p>这同样适用于<code>pair(return_last)</code>,现在只返回{<cd9>}:</p>
<pre><code>>>> cdr(cons(42, 81))
81
</code></pre>
<p>您可能对这些操作的背景感兴趣;<code>car</code>、<code>cdr</code>和<code>cons</code>来自LISP,其中<code>cons a b</code><em>构造了一个带有两个指针的单元格(解释了名称),而<code>car</code>(在IBM 704指令集LISP中,表示寄存器号</em>的地址部分的内容)和<code>cdr</code>(在704语言中,表示寄存器号</em>的减量部分的<em>内容)取这样一个单元的第一部分和剩余部分。见<a href="https://en.wikipedia.org/wiki/CAR_and_CDR" rel="noreferrer">this Wikipedia article on the names</a>。在</p>