1 ноября 2012 г.

Области видимости, углубляемся

Сохранение состояния объемлющей области видимости 
с помощью аргументов по умолчанию:
Из-за отсутствия вложенных областей видимости в инструкциях def в более ранних версиях Python, программный код мог бы терпеть неудачу.
Чтобы разрешить ситуацию, программисты обычно использовали аргументы со значениями по умолчанию для передачи (сохранения) объектов, расположенных в объемлющей области видимости:
def f1():
    x = 88
    def f2(x=x):       # Сохраняет значение переменной x в объемлющей области 
        print(x)       # в виде аргумента
    f2()

f1()                   # Выведет 88
Этот  фрагмент  будет  работать  во  всех  версиях  Python.
-----------------------------------------------------------------
Обратите  внимание,  что вполне  допустимо  вызывать  функцию,  определение которой в тексте программы находится ниже функции, откуда производится вызов, как в данном случае, при условии, что вторая инструкция def будет исполнена до того, как первая функция попытается вызвать ее, – программный код внутри инструкции def не выполняется, пока не будет произведен фактический вызов функции:
>>> def f1():
...     x = 88     # Передача значения x вместо вложения функций 
...     f2(x)      # Опережающие ссылки считаются допустимыми
...
>>> def f2(x):
...    print(x)
...
>>> f1()
88
-----------------------------------------------------------------

Вложенные области видимости и lambda-выражения:
lambdaэто выражение, оно может использоваться там, где не допускается ис
пользование инструкции def, например в литералах списков и словарей.
def func():
    x = 4
    action = (lambda n: x ** n) # запоминается x из объемлющей инструкции def

    return action

x = func()
print(x(2))                     # Выведет 16, 4 ** 2
-----------------------------------------------------------------

lambda и использование значения по умолчанию:
def func():
    x = 4
    action = (lambda n, x=x: x ** n) # Передача x вручную
    return action

Поскольку lambda – это выражения, они естественно (и даже обычно) вкладываются внутрь инструкций def.
-----------------------------------------------------------------

Области видимости и значения по умолчанию применительно к переменным цикла.
Например, создаем список функций, в каждой запоминается текущее значение переменной i из объемлющей области видимости:
>>> def makeActions():
...     acts = []
...     for i in range(5): # Сохранить каждое значение i
...         acts.append(lambda x: i ** x) # Все запомнят последнее значение i!
...     return acts
...
>>> acts = makeActions()
>>> acts[0]
<function <lambda> at 0x012B16B0>

Такой  подход  не  дает  желаемого  результата,  потому  что  поиск  переменной в объемлющей области видимости производится позднее, при вызове вложенных функций, в результате все они получат одно и то же значение (значение, которое  имела  переменная  цикла  на  последней  итерации).  То  есть  каждая функция в списке  будет возвращать  4 во  второй степени, потому что во всех них переменная i имеет одно и то же значение:
>>> acts[0](2)     # Все возвращают 4 ** 2, последнее значение i
16
>>> acts[2](2)     # Здесь должно быть 2 ** 2
16
>>> acts[4](2)     # Здесь должно быть 4 ** 2
16
-----------------------------------------------------------------

Значения по умолчанию вычисляются в момент создания вложенной функции (а не когда она вызывается), поэтому каждая из них сохранит свое собственное значение i:
>>> def makeActions():
...     acts = []
...     for i in range(5):                # Использовать значения по умолчанию
...         acts.append(lambda x, i=i: i ** x) # Сохранить текущее значение i
...     return acts
...
>>> acts = makeActions()
>>> acts[0](2)                             # 0 ** 2
0
>>> acts[2](2)                             # 2 ** 2
4
>>> acts[4](2)                             # 4 ** 2
16
-----------------------------------------------------------------

Произвольное вложение областей видимости
Области види
мости могут вкладываться произвольно, но поиск будет производиться только 

в объемлющих функциях. 
>>> def f1():
...     x = 99
...     def f2():
...         def f3():
...             print(x) # Будет найдена в области видимости f1!

...         f3()
...     f2()
...
>>> f1()
99
Интерпретатор  будет  искать  переменную  в  локальных  областях  видимости всех объемлющих инструкций def, начиная от внутренних к внешним, выше локальной области видимости и ниже глобальной области видимости модуля. 

1 комментарий:

  1. хороший пост, очень просто написано, легко во всем разобрался.

    ОтветитьУдалить