Сохранение состояния объемлющей области видимости
Из-за отсутствия вложенных областей видимости в инструкциях 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 func():
x = 4
action = (lambda n: x ** n) # запоминается x из объемлющей инструкции def
return action
x = func()
print(x(2)) # Выведет 16, 4 ** 2
-----------------------------------------------------------------
lambda и использование значения по умолчанию:
Поскольку 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
Значения по умолчанию вычисляются в момент создания вложенной функции (а не когда она вызывается), поэтому каждая из них сохранит свое собственное значение i:
с помощью аргументов по умолчанию:
Чтобы разрешить ситуацию, программисты обычно использовали аргументы со значениями по умолчанию для передачи (сохранения) объектов, расположенных в объемлющей области видимости:
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, например в литералах списков и словарей.
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, начиная от внутренних к внешним, выше локальной области видимости и ниже глобальной области видимости модуля.
хороший пост, очень просто написано, легко во всем разобрался.
ОтветитьУдалить