2015-10-02 4 views
2

Я занимаюсь классом на языках программирования. Репетитор объяснял о столах вызовов. У меня было сомнение, которое преподаватель не мог объяснить должным образом. Если функция (func1) возвращает вложенную функцию (скажем func2, и пусть func2 использует переменные, определенные в func1). И мы сохраняем возвращаемое значение в некоторой переменной, например, returnFunc. Функция func1 вышла бы после завершения инструкции return в func1. И фрейм стека func1 должен был выскочить из стека вызовов. Теперь мы вызываем returnFunc где-то еще в коде. Но returnFunc использует переменные local для func1, чей фрейм стека больше не существует в стеке вызовов. Как это работает.Реализация стека вызовов в языках программирования

Пример кода питон:

def func1(): 
    a = 3; 
    def func2(): 
     print(a) 
    return func2 

returnedFunc = func1() 
returnedFunc() 

Этот фрагмент кода правильно печатает 3. В то время как я не ожидал, что какое-то значение для мусора, так как func1 больше не существует в стеке вызовов

ответ

0

func2 все еще содержит копию scope который содержал его, назначенный компилятором, поскольку он использует имя, найденное в этой области.

0

Python отличается. Он имеет свой собственный байтовый код, интерпретатор и стек. Он не использует стек C/machine для самого кода Python. Также ваш пример создает новую функцию. Он выделяется как объект в куче, а не в стеке. Таким образом, он существует после возвращения.

6

Переменных & их значения из внешней функции func1 которой внутренняя функция func2 видов использования «упакованы» с func2, когда определена внутренней функция, и что «лексическая среда» приходит с func2, когда func1 возвращает его. func2 - это то, что называется closure (пример, приведенный в верхней части статьи, очень похож на ваш, и немного расширяет его). Вы правы, что func1 копия a вылетает из стека, когда эта функция возвращается, но возвращаемый func2 имеет привязку a к 3, которая будет использоваться, когда она вызывается через returnedFunc(). Python умнее, чем привязать a к тому, что в скором времени будет мусор :)

Чтобы проиллюстрировать это, давайте использовать несколько более сложный пример:

def outer(x): 
    def inner(y): 
     return x+y 
    return inner 

inner3 = outer(3) 
inner5 = outer(5) 

Как и следовало ожидать,

>>> inner3(1) 
4 
>>> inner5(1) 
6 

Вы можете проверить привязки закрытия с помощью inspect.getclosurevars. Обратите внимание на то, что каждая крышка имеет свою собственную копию 'x':

from inspect import getclosurevars 
>>> getclosurevars(inner3) 
ClosureVars(nonlocals={'x': 3}, globals={}, builtins={}, unbound=set()) 
>>> getclosurevars(inner5) 
ClosureVars(nonlocals={'x': 5}, globals={}, builtins={}, unbound=set()) 

Однако, если два замыкания используют один и тот же нелокального переменную, как в вашем примере, переменная будет связано в том же месте. Рассмотрим эту ситуацию (с комментарием по ОП):

def func1(): 
    a = 3 
    def func2(): 
     nonlocal a 
     a += 1 
     return a 

    def func3(): 
     nonlocal a 
     a -= 1 
     return a 

    return func2, func3 

f2, f3 = func1() 

Вызов функции f2 и f3 предполагает, что они используют одинаковое значение a:

>>> f2(), f2(), f3(), f3() 
(4, 5, 4, 3) 

экспертизы атрибут каждого шоу __closure__, что Это действительно так."Клетки" (переплеты) являются одинаковыми, и каждый "указывает на то же" Int объекта:

>>> f2.__closure__ 
(<cell at 0x100380fa8: int object at 0x1002739a0>,) 
>>> f2.__closure__ == f3.__closure__ 
True 

A cell объекта (класса cell) имеет атрибут cell_contents; для f2 и f3cell_contents являются объектами int. Вот еще подтверждение того, что две ячейки указывают на одно и то же:

>>> f2.__closure__[0].cell_contents is f3.__closure__[0].cell_contents 
True 

В самом деле, две ячейки одинаковы:

>>> f2.__closure__[0] is f3.__closure__[0] 
True 
+0

Понял. Вы хотите сказать, что копия 'a', которая упакована в замыкание func2, находится в куче вместо стека. Что означает, что затворы хранятся в куче? – nagavamsikrishna

+0

Да, копия 'a' является частью объекта Python, выделенной кучей. Закрытие - это объекты Python, вызываемые пользователем, и выделены в виде кучи. Разумеется, когда * называется *, стек вызовов входит в игру; но это не то место, где они проживают *. – BrianO

+0

Но что происходит, когда func1 имеет 2 вложенные функции func2 и func3, оба из которых используют одну и ту же переменную, локальную для func1. В таком случае значение 'a' не должно быть« упаковано »с закрытием любого из func2 или func3, но должно быть за пределами закрытий и разделяться func2 и func3. Не так ли? – nagavamsikrishna

Смежные вопросы