Прежде всего: объекты-функции Python объекты первого класса .Оператор def
приводит к созданию нового объекта функции, и вы можете получить этот объект, используя имя функции:
>>> def foo():
... return 'foo was called'
...
>>> foo
<function foo at 0x11b3d8ae8>
Это означает, что вы можете назначить этот объект и другим именам, и выможет передавать их как параметры для вызовов функций.Затем вы можете позже вызвать объект функции, добавив (...)
к ссылке:
>>> bar = foo
>>> bar()
'foo was called'
Имя функции, назначенное в текущем пространстве имен.В модуле это глобальные переменные, но в такой функции, как cons
, имя добавляется как локальное.return pair
в функции cons
затем возвращает вызывающему объекту функциональный объект pair
.
И параметры функции, такие как f
и a
и b
, также являются переменными;если вы передадите объект функции в качестве параметра, то parameter_name(...)
вызовет paramater_name
и передаст любые аргументы в части ...
.f(a, b)
вызывает f
и переходит в a
и b
.
Следующий пункт, который нужно понять, это замыкания ;замыкания - это дополнительное пространство имен, присоединенное к объектам функций, для переменных из окружающей области.
В следующем примере spam
- это имя в замыкании функции bar
:
>>> def foo():
... spam = 'Vikings'
... def bar():
... return spam
... return bar
...
>>> foo()
<function foo.<locals>.bar at 0x11b44bf28>
>>> foo()()
'Vikings'
Вызов foo()
возвращает новый объект функции;bar()
функция внутри foo()
.Вызов этого возвращенного объекта функции приводит к 'Vikings'
, значению переменной spam
в функции foo
.Как bar()
получил доступ к этому?Через замыкание:
>>> foo().__closure__
(<cell at 0x11b3c05b8: str object at 0x11b469180>,)
>>> foo().__closure__[0].cell_contents
'Vikings'
Таким образом, вложенные функции могут иметь доступ к именам из окружающей области через замыкания.(Примечание: это не значение , которое хранится в замыкании, это переменная . Переменная может изменяться со временем, и, как и другие переменные, получающие доступ к имени позже, будут отражать новоезначение; если spam
было изменено позже, повторный вызов bar()
вернет новое значение).
Теперь вашей функции: cons()
создает внутреннюю функцию pair()
, а pair()
имеетдоступ к параметрам a
и b
через его закрытие:
>>> 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
Функция pair()
принимает параметр f
, а вызывает этот параметр, передавая a
и b
.Давайте посмотрим, что произойдет, когда мы передадим print
.
print
- это тоже функция, это объект, который вы можете вызвать, и он записывает аргументы в консоль с пробелами между ними:
>>> print
<built-in function print>
>>> print('arg1', 'arg2')
arg1 arg2
Если вы передали это функции pair()
, которую возвращает cons()
, вы можете увидеть, что делает f(a, b)
:
>>> pair_42_81(print)
42 81
print
, переданное в pair()
, присваивается f
, а f(a, b)
- это то же самое, что и print(a, b)
.
Мы можем видеть, что print()
был вызван, потому что значения записаны в консоль.Но вы также можете создать функцию, которая возвращает новое значение.Скажем, у вас есть функция, которая складывает два числа и возвращает это значение:
>>> def add(first, second):
... return first + second
...
>>> add(42, 81)
123
>>> pair_42_81(add)
123
Мы можем вызвать функцию напрямую, и возвращается 123
, или мы можем pair_42_81()
сделать это за наси тот же результат возвращается к нам.Просто!
Все это работает, потому что функции являются объектами и могут передаваться как другие переменные, а также потому что pair_42_81
имеет a = 42
и c = 81
, хранящиеся в замыкании, и будет использовать их для вызова заданногообъект f
с этими двумя аргументами.
Далее идут cdr()
и car()
, которые будут возвращать либо первый, либо последний элемент пары.Если cons(a, b)
производит pair(f)
, возвращая f(a, b)
, то cdr()
и car()
должны создать функцию для передачи pair()
, которая будет извлекать либо a
, либо b
.
* 1117.* Так что создайте вложенную функцию в каждой, и
cdr()
и
car()
вызовите
pair()
с этой функцией.Вложенная функция выполняет выбор
a
или
b
и возвращает это значение.Затем верните результат вызова во внешний мир:
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)
pair(return_first)
звонков return_first(a, b)
, a
возвращается, и car()
может вернуть его вызывающему:
>>> car(cons(42, 81))
42
и то же самое относится к pair(return_last)
, только теперь возвращается b
:
>>> cdr(cons(42, 81))
81
Вас могут заинтересовать предпосылки этих операций;car
, cdr
и cons
взяты из LISP, где cons a b
Создает ячейку с двумя указателями (объясняя имя) и car
(что означает Содержание адресачасть регистрационного номера в наборе команд IBM 704, на котором был создан LISP) и cdr
(что означает содержимое декретной части регистрационного номера на языке 704), занимают первую и остальные частитакой клетки.См. эту статью в Википедии об именах .